Найти строки, в которых значение во вложенном JSON документе больше, чем INT - PullRequest
3 голосов
/ 10 января 2020

Я всего PostgreSQL новичок ie. Но мне кажется интересным использовать json / jsonb в реляционной базе данных.

// просто для информации: я пытаюсь реализовать это с помощью C#. NET Core WebAPI с Dapper

Я хотел бы иметь таблицу с (многомерным) столбцом json, например:

<strong> id                                   </strong>|<strong> data                                                             </strong>
 _____________________________________|_________________________________________________________________________________
                                      |                                                                                 
 4BF30FE6-D7DD-480B-8592-DC9676576FEF | { timestamps:{ "created":1578614541, "modified":1578615707 }, "type":"single"}  
 1AC2CD8F-09D0-456C-9FD4-B63E354BD324 | { timestamps:{ "created":1578614676, "modified":1578615921 }, "type":"multiple"}
 50AD2D82-5919-4555-BCC2-B24E0DE24263 | { timestamps:{ "created":1578614552, "modified":1578615784 }, "type":"single"}  
 8C3BE671-17D1-49FD-A891-D5E69FDF7FC2 | { timestamps:{ "created":1578614685, "modified":1578615173 }, "type":"single"}   

И я хочу получить все идентификаторы, которые имеют data :: timestamps.created больше 1578614670.

Псевдокод:

SELECT id, data FROM table WHERE data::timestamps.created > 1578614670;
<strong> id                                   </strong>|<strong> data                                                             </strong>
 _____________________________________|_________________________________________________________________________________
                                      |                                                                                 
 1AC2CD8F-09D0-456C-9FD4-B63E354BD324 | { timestamps:{ "created":<strong>1578614676</strong>, "modified":1578615921 }, "type":"multiple"}
 8C3BE671-17D1-49FD-A891-D5E69FDF7FC2 | { timestamps:{ "created":<strong>1578614685</strong>, "modified":1578615173 }, "type":"single"}   

Есть ли простой способ добиться этого?

Ответы [ 2 ]

4 голосов
/ 10 января 2020

Для перехода на несколько уровней вложенности полезен оператор #>> . Затем приведите извлеченные text к соответствующим цифрам c типам перед сравнением.

SELECT id, data FROM tbl
WHERE (data #>> '{timestamps,created}')::numeric > 1578614670;

Эквивалентно:

...
WHERE (data -> 'timestamps' ->> 'created')::numeric > 1578614670;

numeric - это безопасная ставка. Если вы знаете , что все числа в created меньше, чем 2 ^ 31 или 2 ^ 63, вы можете использовать integer или bigint соответственно. В вашем примере показаны действительные числа c литералов, но это должно содержать все извлеченных значений.

Чтобы сделать это быстро, рассмотрим индекс выражения, подобный предложенному Майлзу, с приведением к соответствующий тип цифры c - но с соответствующими круглыми скобками.

CREATE INDEX tbl_data_created_idx
ON tbl (((data #>> '{timestamps,created}')::numeric)); -- all parentheses required

Связанные:

Здесь все работает для типов json или jsonb, все в этом отношении.

db <> fiddle здесь ( демонстрируя оба)

2 голосов
/ 10 января 2020

Редактировать: Удалены ошибки, на которые указал Эрвин (и проголосовал Ответ Эрвина ). Оставьте остальную часть ответа, так как другая информация может быть полезной.

Следующие действия будут выполнять то, что вы просите. Вот таблица.

CREATE TABLE example (
  id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
  data jsonb NOT NULL
);

И после того, как вы вставили данные, он выполнит ваш запрос.

SELECT id, data
FROM "table"
WHERE (data->'timestamps'->>'created')::int8 > 1578614670;

Это извлечет «созданную» запись из JSONB столбец в виде текста, а затем преобразуется в 8-байтовое целое число (64-разрядное целое число). Для последовательного сканирования большой таблицы это может быть довольно медленным, поэтому вам понадобится индекс выражения .

CREATE INDEX created_idx ON "table" ((data->'timestamps'->>'created')::int8);
...