Postgres (12.2) Настройка:
CREATE TABLE public.test_table (
id int NOT NULL,
value_type text NOT NULL,
value text NOT NULL
);
INSERT INTO public.test_table
(id, value_type, value)
VALUES (1, 'string', 'a'),
(2, 'json', '{"hello":"world"}'),
(3, 'json', '{"color":"blue"}');
Начальные запросы:
select value::jsonb as json_value from test_table where value_type = 'json'
json_value |
------------------|
{"hello": "world"}|
{"color": "blue"} |
Но меня интересуют только те, которые имеют 'color' .
Перемещение его в подзапрос, чтобы я мог получить только 'color', также просто отлично:
select only_json.json_value
from(
select value::jsonb as json_value from test_table where value_type = 'json'
) only_json
where only_json.json_value ? 'color' = true
json_value |
------------------|
{"color": "blue"} |
Теперь давайте разберем эту основную таблицу на две части, и внезапно фактически тот же запрос имеет проблемы:
CREATE TABLE public.test_table (
id INT PRIMARY KEY,
value TEXT NOT NULL
);
CREATE TABLE public.test_types (
id INT PRIMARY KEY REFERENCES public.test_table (id),
value_type TEXT NOT NULL
);
INSERT INTO public.test_table
(id, value)
VALUES (1, 'a'),
(2, '{"hello":"world"}'),
(3, '{"color":"blue"}');
insert into public.test_types
(id, value_type)
values (1, 'string'),
(2, 'json'),
(3, 'json');
Теперь этот запрос:
select id, value from (
select id, value::jsonb from public.test_table natural join public.test_types
where value_type = 'json') only_json
возвращает, как и ожидалось:
id|value |
--|------------------|
2|{"hello": "world"}|
3|{"color": "blue"} |
Но как только я прикрепляю в выражении where это терпит неудачу:
select id, value from (
select id, value::jsonb from public.test_table natural join public.test_types
where value_type = 'json') only_json
where only_json.value ? 'color' = true
SQL Error [22P02]: ERROR: invalid input syntax for type json
Detail: Token "a" is invalid.
Where: JSON data, line 1: a
Это каким-то образом воскресило значение 'a', которое было хорошо устранено до этого предложения where. Так что же дает? Почему объединение заставляет его применять последнее условие where (которое должно происходить логически последним) слишком рано? Неудачные обходные пути, которые я пробовал:
- Использование левого соединения вместо естественного соединения.
- Применение
where value_type = 'json'
к объединенной таблице перед объединением. - Перемещение его на «с».
- Создание представления с последующим применением условия where к элементу выбора из представления.
- Создание столбца с помощью выбора с именем
is_color_holder
с помощью SELECT only_json.value ? 'color' as is_color_holder
, Этот столбец заполняется правильно, но если я использую предложение where, WHERE is_color_holder = true
, я получаю ту же ошибку. - Повторение выражения
value_type='json'
в условии проблемати c where
. - Перемещение приведенного ниже подзапроса.
- Замена объединения на
where id in (select id from public.test_types where value_type = 'json')
- Объединения в стиле запятой.
- Сначала центрируйте запрос вокруг таблицы типов, а затем объединяйте тип значения после того, как типы уже отфильтрованы.
Это ошибка, о которой я должен сообщить postgres? Я что-то упустил?
Редактировать: мне удалось один обходной путь. Смотрите мой ответ для более подробной информации. Тем не менее все еще ищу лучший ответ.
select id, value from (
select id, case when value_type = 'json' then value::jsonb else to_jsonb(value) end as value, value_type from
public.test_table natural join public.test_types
where value_type = 'json') as_json
where value ? 'color' = true
id|value |
--|-----------------|
3|{"color": "blue"}|