Извлечение данных из массива объектов JSON для определенных значений объекта - PullRequest
0 голосов
/ 11 мая 2019

В моей таблице есть столбец типа JSON, который содержит массив объектов, описывающих смещения времени:

[
  {
    "type": "start"
    "time": 1.234
  },
  {
    "type": "end"
    "time": 50.403
  }
]

Я знаю, что могу извлечь их с JSON_EACH() и JSON_EXTRACT():

CREATE TEMPORARY TABLE Items(
  id INTEGER PRIMARY KEY,
  timings JSON
);

INSERT INTO Items(timings) VALUES
  ('[{"type": "start", "time": 12.345}, {"type": "end", "time": 67.891}]'),
  ('[{"type": "start", "time": 24.56}, {"type": "end", "time": 78.901}]');

SELECT
  JSON_EXTRACT(Timings.value, '$.type'),
  JSON_EXTRACT(Timings.value, '$.time')
FROM
  Items,
  JSON_EACH(timings) AS  Timings;

Возвращает таблицу вроде:

start    12.345
end      67.891
start    24.56
end      78.901

Что мне действительно нужно, так это:

  1. Найдите время определенных типов. (Найдите первый объект в массиве, который соответствует условию.)
  2. Возьмите эти данные и выберите их в виде столбца с остальной таблицей.

Другими словами, я ищу таблицу, которая выглядит следующим образом:

id        start        end
-----------------------------
0         12.345       67.891
1         24.56        78.901

Я надеюсь на какой-то запрос вроде этого:

SELECT
  id,
  JSON_EXTRACT(timings, '$.[type="start"].time'),
  JSON_EXTRACT(timings, '$.[type="end"].time')
FROM Items;

Есть ли способ использовать path в функциях JSON, чтобы выбрать то, что мне нужно? Или каким-то другим способом изменить то, что я имею в первом примере, чтобы применить к таблице?

1 Ответ

1 голос
/ 12 мая 2019

Одна возможность:

WITH cte(id, json) AS
  (SELECT Items.id
        , json_group_object(json_extract(j.value, '$.type'), json_extract(j.value, '$.time'))
   FROM Items
   JOIN json_each(timings) AS j ON json_extract(j.value, '$.type') IN ('start', 'end')
   GROUP BY Items.id)
SELECT id
     , json_extract(json, '$.start') AS start
     , json_extract(json, '$.end') AS "end"
FROM cte
ORDER BY id;

, которая дает

id          start       end
----------  ----------  ----------
1           12.345      67.891
2           24.56       78.901

Другая возможность, которая использует оконные функции, добавленные в sqlite 3.25, и избегает создания промежуточных объектов JSON:

SELECT DISTINCT Items.id
              , max(json_extract(j.value, '$.time'))
                 FILTER (WHERE json_extract(j.value, '$.type') = 'start') OVER ids AS start
              , max(json_extract(j.value, '$.time'))
                 FILTER (WHERE json_extract(j.value, '$.type') = 'end') OVER ids AS "end"
FROM Items
JOIN json_each(timings) AS j ON json_extract(j.value, '$.type') IN ('start', 'end')
WINDOW ids AS (PARTITION BY Items.id)
ORDER BY Items.id;

Ключ использует предложение ON из JOIN, чтобы ограничить результаты только двумя объектами в каждом массиве, который вас интересует, а затем объединить эти до двух строк для каждого Items.id в один спара разных подходов.

...