Как привести только часть таблицы с помощью одной команды SQL в PostgreSQL - PullRequest
1 голос
/ 18 марта 2020

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

У меня возникли проблемы, когда я попытался сравнить информацию (bigint хранится в виде строки) с внешним значением (например, '9'> '11'). Когда я пытался привести столбец, база данных возвращала ошибку (не все значения в столбце являются преобразуемыми, например, дата-время или обычный текст). Также, когда я пытаюсь привести только результат команды запроса, я получаю ошибку приведения.

Я получаю таблицу со строками, которые можно преобразовать, с помощью этой команды:

SELECT information.id as id, item.information::bigint as item
FROM information
INNER JOIN item
ON information.id = item.informationid
WHERE information.type = 'task'

Полученные строки отображаются только текст, который является кастуемым. Когда я добавляю его в другую команду, это приводит к ошибке.

SELECT x.id FROM (
    SELECT information.id as id, item.information::bigint as item
    FROM information
    INNER JOIN item
    ON information.id = item.informationid
    WHERE information.type = 'task'
) AS x
WHERE x.item > '0'::bigint

В соответствии с ошибкой база данных пыталась привести все строки в таблице.

Ответы [ 2 ]

1 голос
/ 18 марта 2020

Технически это происходит потому, что оптимизатор считает WHERE x.item > '0'::bigint гораздо более эффективным фильтром, чем information.type = 'task'. Таким образом, при сканировании таблицы условие WHERE x.item > '0'::bigint выбирается в качестве предиката. Это мышление не является неправильным, но заставит вас впасть в эту, казалось бы, нелогичную проблему.

Предложение Гордона использовать CASE WHEN inf.type = 'task' THEN i.information::bigint END может этого избежать, но, тем не менее, иногда оно может разрушить вашу идею поставить это под запрос и требуют, чтобы одно и то же условие было написано дважды.

Забавный трюк, который я пытался использовать OUTER APPLY:

SELECT x.* FROM (SELECT 1 AS dummy) dummy
OUTER APPLY (
    SELECT information.id as id, item.information::bigint AS item
    FROM information
    INNER JOIN item
    ON information.id = item.informationid
    WHERE information.type = 'task'
) x
WHERE x.item > '0'::bigint

Извините, что я проверил только версию сервера SQL этого , Я понимаю, что PostgreSQL не имеет ВНЕШНЕГО ПРИМЕНЕНИЯ, но эквивалент должен быть:

SELECT x.* FROM (SELECT 1 AS dummy) dummy
LEFT JOIN LATERAL (
    SELECT information.id as id, item.information::bigint AS item
    FROM information
    INNER JOIN item
    ON information.id = item.informationid
    WHERE information.type = 'task'
) x ON true
WHERE x.item > '0'::bigint

(ссылка этот вопрос )

Наконец, более аккуратно, но менее гибкий метод - добавить подсказку оптимизатора, чтобы отключить ее, чтобы оптимизатор запустил запрос так, как он написан.

1 голос
/ 18 марта 2020

Это прискорбно. Попробуйте использовать выражение case:

SELECT inf.id as id,
       (CASE WHEN inf.type = 'task' THEN i.information::bigint END) as item
FROM information inf JOIN
     item i
     ON inf.id = i.informationid
WHERE inf.type = 'task';

Нет гарантии, что фильтр WHERE будет применен до SELECT. Однако CASE гарантирует порядок оценки, поэтому он безопасен.

...