Как построить запрос SQLite с предложением WHERE внутри столбца JSON-массива-объектов?

В связи с этим вопросом я пытаюсь построить высокопроизводительный* запрос SQLite на столбце JSON.
Рассматривая БД с этой схемой (псевдокод):

** Table **
id   content  json
1    abc      [{start: some-unix-ts, end: some-unix-ts},{start: some-unix-ts, end: some-unix-ts}, between 1 to 1K+ items]
2    xyz      ...

Можно ли написать запрос, извлекающий все события, где содержимое = xyz И где есть хотя бы одно событие в будущем или между двумя временными метками. В псевдокоде:

SELECT * FROM events WHERE content = xyz AND start > some-unix-ts

*) Под высокой производительностью я подразумеваю (не имея опыта работы с SQL): если на теоретически огромной БД это вообще неэффективно, позвольте мне сейчас!

SQLite был разработан для удобства, а не для производительности, и он отстой для огромных баз данных. Если вам нужно высокопроизводительное решение для больших наборов данных, вам нужно использовать настоящий сервер базы данных, такой как MySQL или Postgres. Их не намного сложнее настроить, чем SQLite.

Tim Roberts 29.04.2023 20:48

Кроме того, базовый SQLite не поддерживает столбцы JSON. У него нет полной системы типов — он хранит все строки (включая числа). Доступно расширение JSON, но поскольку оно не является основным, оно никогда не будет супер быстрым.

Tim Roberts 29.04.2023 20:49

Если у вас а) есть одна строка для каждого элемента этих json-массивов, б) вы используете формат даты , понятный SQLite , например, метку времени unix или строку iso-8601, которая естественным образом сортируется, у вас будет лучшее время.

Shawn 29.04.2023 20:54

@TimRoberts SQLite поддерживает типы помимо строк и сохраняет числовые типы как числа, а не строки (в зависимости от определения таблицы, см. sqlite.org/datatype3.html#type_affinity). И в наши дни функции json включены по умолчанию (и всегда были основной частью SQLite). Не то чтобы OP должен хранить здесь свои данные в массивах json.

Shawn 29.04.2023 20:58

Ваши объекты JSON искажены для SQLite, потому что и пути, и значения не заключены в кавычки. Кроме того, значения имеют несопоставимый формат MM-YYYY. Вместо этого вы должны использовать YYYY-MM, если вы действительно заботитесь о производительности, поскольку использование строковых функций для преобразования текущего формата в сопоставимый в предложении WHERE приведет к значительному снижению производительности. Но даже если вы внесете эти изменения, я не верю, что высокопроизводительный запрос выполним для большой базы данных.

forpas 29.04.2023 21:06

@forpas: я обновил свой вопрос (даты действительно указаны в формате метки времени + это, конечно, хорошо отформатированный Json)

charnould 29.04.2023 22:53

Если у вас есть несколько значений start для данного id, вы хотите, чтобы они все или только одно было > some-unix-ts?

Nick 30.04.2023 00:58

И является ли ваш ожидаемый результат только значениями start, которые являются > some-unix-ts, или всей записью для этого id?

Nick 30.04.2023 01:16

@ Ник, я добавил некоторые детали. Запрос должен возвращать events, где, например, есть хотя бы одно вхождение в будущем.

charnould 30.04.2023 06:38
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
0
9
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете добиться желаемого результата, используя json_each, чтобы разделить значения временных интервалов в столбце json, а затем сравнить их с интересующим вас временем. Например (используя расширенный dbfiddle из ответа на ваш предыдущий вопрос):

SELECT ev.id, ev.content, json_each.value
FROM ev, json_each(ev.json)
WHERE ev.content = 'xyz' 
  AND json_each.value->>'$.start' > '2022-07'

Выход:

id  content value
2   xyz     {"start":"2022-11","end":"2022-12"}
3   xyz     {"start":"2022-09","end":"2022-10"}

Однако это не будет особенно эффективным. Было бы намного лучше вернуться к исходным таблицам и вместо этого запросить их:

SELECT e.*, t.start, t.end
FROM events e
JOIN timeslots t ON t.id = e.id
WHERE e.content = 'xyz' AND t.start > '2022-07'

Выход:

id  content start   end
2   xyz     2022-11 2022-12
3   xyz     2022-09 2022-10

Демо на dbfiddle

Вам нужно будет заменить сравнения start тем, что подходит для ваших фактических данных временного интервала.

@charnould ответил на твой вопрос? Если нет, можете ли вы предоставить дополнительную информацию, чтобы помочь ответить на него?

Nick 03.05.2023 07:30

Спасибо. Я денормализую свои данные: (i) сохраняю timeslotsстолбец в events как JSON, чтобы упростить API запросов для некоторых людей, и (ii) сохраняю timeslots таблицу, чтобы получить производительный запрос.

charnould 04.05.2023 14:57

@charnould звучит как идеальный баланс размера и производительности.

Nick 05.05.2023 01:40

Другие вопросы по теме