PostgreSQL - ошибка коррелированного подзапроса? - PullRequest
1 голос
/ 09 января 2009

У меня такой запрос:

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? Или, если нет, есть ли другой способ достижения этого результата?

Ответы [ 5 ]

10 голосов
/ 09 января 2009

Несколько важных моментов об использовании 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.


Re: вопрос в комментарии:

Разница в том, что предложение 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;

Итак, подведем итог: запросы выполняются ядром базы данных в соответствии с последовательностью шагов:

  1. Создание набора строк из таблицы (таблиц), включая любые строки, созданные с помощью JOIN.
  2. Оцените WHERE условия по набору строк, отфильтровывая строки, которые не совпадают.
  3. Вычислить выражения в списке выбора для каждого в наборе строк.
  4. Применение псевдонимов столбцов (обратите внимание, что это отдельный шаг, что означает, что вы не можете использовать псевдонимы в выражениях в списке выбора).
  5. Сжатие групп в одну строку для каждой группы согласно пункту GROUP BY.
  6. Оцените HAVING условий по группам, отфильтровывая группы, которые не соответствуют.
  7. Сортировать результат в соответствии с предложением ORDER BY.
3 голосов
/ 29 июля 2009

Я хотел бы отметить, что в PostgreSQL нет способа использовать псевдоним столбца в предложении have.

т.е.

ВЫБЕРИТЕ usr_id КАК my_id ОТ ИМЕНИ пользователя my_id = 1

Не будет работать.

Еще один пример, который не будет работать:

ВЫБРАТЬ su.usr_id КАК my_id, COUNT (*) КАК val из ОТ sys_user AS su GROUP BY su.usr_id HAVING val> = 1

Произойдет та же ошибка: столбец val неизвестен.

Я подчеркиваю это, потому что Билл Карвин написал что-то не совсем верное для Postgres:

«Вы не можете использовать псевдонимы столбцов в предложении WHERE, но вы можете использовать в предложении HAVING. Это и есть причина возникшей ошибки.»

3 голосов
/ 09 января 2009

Все остальные предложения будут работать, но чтобы ответить на ваш основной вопрос, достаточно написать

  SELECT id  From T2
  Group By Id
  Having Count(*) = 5
1 голос
/ 09 января 2009

Я думаю, вы могли бы просто переписать ваш запрос следующим образом:

SELECT t1.id
FROM t1
WHERE (SELECT COUNT(t2.id)
     FROM t2
     WHERE t2.id = t1.id
          ) = 5;
0 голосов
/ 09 января 2009

попробуйте

SELECT t1.id,
    (SELECT COUNT(t2.id) as myCount
     FROM t2
     WHERE t2.id = t1.id and myCount=5
          ) as num_things
FROM t1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...