Сбой подзапроса, связанного с PostgreSQL?
-
06-07-2019 - |
Вопрос
У меня есть запрос, подобный этому:
SELECT t1.id,
(SELECT COUNT(t2.id)
FROM t2
WHERE t2.id = t1.id
) as num_things
FROM t1
WHERE num_things = 5;
Цель состоит в том, чтобы получить идентификатор всех элементов, которые появляются 5 раз в другой таблице.Однако я получаю эту ошибку:
ERROR: column "num_things" does not exist
SQL state: 42703
Вероятно, я делаю здесь что-то глупое, поскольку я несколько новичок в базах данных.Есть ли способ исправить этот запрос, чтобы я мог получить доступ num_things
?Или, если нет, есть ли какой-либо другой способ достижения этого результата?
Решение
Я думаю, вы могли бы просто переписать свой запрос следующим образом:
SELECT t1.id
FROM t1
WHERE (SELECT COUNT(t2.id)
FROM t2
WHERE t2.id = t1.id
) = 5;
Другие советы
Несколько важных моментов об использовании SQL:
- Вы не можете использовать псевдонимы столбцов в предложении WHERE, но вы можете использовать их в предложении HAVING.Это причина ошибки, которую вы получили.
- Вы можете лучше подсчитать, используя JOIN и GROUP BY, чем используя коррелированные подзапросы.Так будет намного быстрее.
- Используйте предложение HAVING для фильтрации групп.
Вот как я бы написал этот запрос:
SELECT t1.id, COUNT(t2.id) AS num_things
FROM t1 JOIN t2 USING (id)
GROUP BY t1.id
HAVING num_things = 5;
Я понимаю, что этот запрос может пропустить JOIN
с t1, как в решении Чарльза Бретаны.Но я предполагаю, что вы можете захотеть, чтобы запрос включал некоторые другие столбцы из t1.
Ре:вопрос в комментарии:
Разница в том, что WHERE
предложение вычисляется в строках, перед GROUP BY
уменьшает количество групп до одной строки для каждой группы.Тот Самый HAVING
предложение оценивается после формирования групп.Таким образом, вы не можете, например, изменить COUNT()
группы с помощью HAVING
;вы можете исключить только саму группу.
SELECT t1.id, COUNT(t2.id) as num
FROM t1 JOIN t2 USING (id)
WHERE t2.attribute = <value>
GROUP BY t1.id
HAVING num > 5;
В приведенном выше запросе, WHERE
фильтрует строки, соответствующие условию, и HAVING
фильтры для групп, количество которых не менее пяти.
Момент, который приводит большинство людей в замешательство, заключается в том, что у них нет GROUP BY
оговорка, так что это кажется Нравится HAVING
и WHERE
являются взаимозаменяемыми.
WHERE
вычисляется перед выражениями в списке выбора.Это может быть неочевидно, потому что синтаксис SQL ставит список выбора на первое место.Таким образом, вы можете сэкономить много дорогостоящих вычислений, используя WHERE
чтобы ограничить количество строк.
SELECT <expensive expressions>
FROM t1
HAVING primaryKey = 1234;
Если вы используете запрос, подобный приведенному выше, выражения в списке выбора вычисляются для каждая строка, только для того , чтобы отбросить большую часть результатов из - за HAVING
состояние.Однако приведенный ниже запрос вычисляет выражение только для одиночный ряд соответствующий WHERE
состояние.
SELECT <expensive expressions>
FROM t1
WHERE primaryKey = 1234;
Итак, подводя итог, запросы выполняются ядром базы данных в соответствии с серией шагов:
- Сгенерировать набор строк из таблицы (таблиц), включая любые строки, созданные
JOIN
. - Оценивать
WHERE
условия для набора строк, отфильтровывая строки, которые не совпадают. - Вычислите выражения в списке выбора для каждого из набора строк.
- Примените псевдонимы столбцов (обратите внимание, что это отдельный шаг, который означает, что вы не можете использовать псевдонимы в выражениях в списке выбора).
- Сведите группы к одной строке для каждой группы в соответствии с
GROUP BY
оговорка. - Оценивать
HAVING
условия для групп, отфильтровывающие группы, которые не совпадают. - Отсортируйте результат в соответствии с
ORDER BY
оговорка.
Все остальные предложения сработают, но для ответа на ваш основной вопрос достаточно написать
SELECT id From T2
Group By Id
Having Count(*) = 5
Я хотел бы упомянуть, что в PostgreSQL нет способа использовать столбец с псевдонимами в предложении having.
т. е.
ВЫБЕРИТЕ usr_id В КАЧЕСТВЕ my_id У пользователя, ИМЕЮЩЕГО my_id = 1
Не сработает.
Еще один пример, который не сработает:
ВЫБЕРИТЕ su.usr_id КАК my_id, ПОСЧИТАЙТЕ (*) КАК val ИЗ sys_user КАК ГРУППУ su ПО su.usr_id, ИМЕЮЩЕМУ значение >= 1
Возникнет та же ошибка:значение столбца неизвестно.
Я подчеркиваю это, потому что Билл Карвин написал что-то не совсем верное для Postgres:
"Вы не можете использовать псевдонимы столбцов в предложении WHERE, но вы можете в предложении HAVING.Это причина ошибки, которую вы получили ".
попробуйте это
SELECT t1.id,
(SELECT COUNT(t2.id) as myCount
FROM t2
WHERE t2.id = t1.id and myCount=5
) as num_things
FROM t1