Запит JSONB в PostgreSQL


14

У мене є таблиця, personsяка містить два стовпці, стовпчик на idоснові JSONB і dataця таблиця була створена для демонстраційних цілей, щоб грати з підтримкою JSON PostgreSQL).

Тепер, передбачається, він містить два записи:

1, { name: 'John', age: 30 }
2, { name: 'Jane', age: 20 }

Тепер, напевно, я хочу отримати ім'я кожної людини старше 25 років. Я спробував:

select data->'name' as name from persons where data->'age' > 25

На жаль, це призводить до помилки. Я можу це вирішити, використовуючи ->>замість ->, але тоді порівняння вже не працюють так, як очікувалося, оскільки не порівнюються числа, а їх представлення у вигляді рядків:

select data->'name' as name from persons where data->>'age' > '25'

Я тоді зрозумів, що я на самом деле може вирішити проблему шляхом використання ->та приведення до int:

select data->'name' as name from persons where cast(data->'age' as int) > 25

Це працює, але це не так приємно, що я маю знати фактичний тип (тип документа ageв JSON все- numberтаки є , тому чому PostgreSQL не може розібратися в цьому сам?).

Потім я зрозумів, що якщо я вручну перетворяться на textвикористання ::синтаксису, все також працює, як і очікувалося - хоча ми зараз знову порівнюємо рядки.

select data->'name' as name from persons where data->'age'::text > '25'

Якщо я спробую це з іменем замість віку, воно не вийде:

select data->'name' as name from persons where data->'name'::text > 'Jenny'

Це призводить до помилки:

недійсний синтаксис введення для типу json

Досить очевидно, я щось тут не отримую. На жаль, досить важко знайти будь-які реальні приклади використання JSON з PostgreSQL.

Якісь підказки?


1
У data->'name'::text, ви кидаєте 'name'рядок до тексту, а не до результату. Ви не отримуєте помилки при порівнянні, '25'оскільки 25це дійсна літера JSON; але Jennyце не так (хоча "Jenny"б і було).
chirlu

Дякую, ось таке рішення :-). Я переплутався 'Jenny'з '"Jenny"'.
Голо Роден

Відповіді:


15

Це не працює, тому що він намагається надати jsonbзначення integer.

select data->'name' as name from persons where cast(data->'age' as int) > 25

Це насправді спрацювало б:

SELECT data->'name' AS name FROM persons WHERE cast(data->>'age' AS int) > 25;

Або коротше:

SELECT data->'name' AS name FROM persons WHERE (data->>'age')::int > 25;

І це:

SELECT data->'name' AS name FROM persons WHERE data->>'name' > 'Jenny';

Схоже , плутанина з двома операторами ->і->> і старшинством операторів . Лита ::зв'язується сильніше, ніж оператори json (b).

Визначте тип динамічно

Це більш цікава частина вашого запитання:

тип віку в документі JSON все одно є числом, тож чому PostgreSQL не може зрозуміти це сам?

SQL є строго набраною мовою, вона не дозволяє оцінювати integerодин і той же вираз в одному ряду і textв наступному. Але оскільки вас цікавить лише booleanрезультат тесту, ви можете обійти це обмеження із CASEвиразом, який розгортається залежно від результату jsonb_typeof():

SELECT data->'name'
FROM   persons
WHERE  CASE jsonb_typeof(data->'age')
        WHEN 'number'  THEN (data->>'age')::numeric > '25' -- treated as numeric
        WHEN 'string'  THEN data->>'age' > 'age_level_3'   -- treated as text
        WHEN 'boolean' THEN (data->>'age')::bool           -- use boolean directly (example)
        ELSE FALSE                                         -- remaining: array, object, null
       END;

Нетипізований рядок, буквально праворуч від >оператора, автоматично приводиться до відповідного типу значення зліва. Якщо ви помістите туди введене значення, тип повинен збігатися або вам доведеться чітко його передати, якщо тільки в системі не зареєстровано адекватний неявний формат.

Якщо ви знаєте, що всі числові значення є насправді integer, ви також можете:

... (data->>'age')::int > 25 ...

що виражає основний вираз sqlalchemy для наведеного вище порівняння оператора select. s = select ([issues]). where (issues.c.id == mid) .select_from (issues, ..... externaljoin (issues.c.data ['type_id'] == mtypes.c.id) ) ... Ось тип даних data.c.data jsonb і порівнюється з mtypes.c.id цілого типу
user956424
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.