Использование предложения DISTINCT для фильтрации данных, но все же извлекает другие поля, которые не являются DISTINCT - PullRequest
13 голосов
/ 06 октября 2010

Я пытаюсь написать запрос в Postgresql, который извлекает набор упорядоченных данных и фильтрует их по отдельному полю.Мне также нужно извлечь несколько других полей из той же строки таблицы, но они должны быть исключены из отдельной оценки.пример:

  SELECT DISTINCT(user_id) user_id, 
         created_at 
    FROM creations 
ORDER BY created_at   
   LIMIT 20

Мне нужно, чтобы user_id было DISTINCT, но мне все равно, уникальна ли дата create_at или нет.Поскольку дата create_at включена в оценку, я получаю дубликат user_id в моем наборе результатов.

Кроме того, данные должны быть упорядочены по дате, поэтому использование DISTINCT ON здесь не вариант,Требовалось, чтобы поле DISTINCT ON было первым полем в предложении ORDER BY, и это не дает результатов, к которым я стремлюсь.

Как правильно использовать предложение DISTINCT, но ограничить его область действиятолько одно поле при выборе других полей?

Ответы [ 5 ]

5 голосов
/ 06 октября 2010

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

Но PostgreSQL имеет нестандартное расширение для SQL, чтобы учесть то, что вы запрашиваете: DISTINCT ON (expr).

SELECT DISTINCT ON (user_id) user_id, created_at 
FROM creations 
ORDER BY user_id, created_at   
LIMIT 20

Вы должны включить различные выражения в крайнюю левую часть вашего предложения ORDER BY.

Подробнее см. Руководство по DISTINCT Clause .информация.

4 голосов
/ 06 октября 2010

Если вы хотите, чтобы самый последний созданный_ат для каждого пользователя, то я предлагаю вам агрегировать так:

SELECT user_id, MAX(created_at)
FROM creations
WHERE ....
GROUP BY user_id
ORDER BY created_at DESC

Это вернет самый последний созданный_ат для каждого user_id. Если вам нужны только первые 20, добавьте

LIMIT 20

РЕДАКТИРОВАТЬ: Это в основном то же самое, что Unreason сказал выше ... определить, из какой строки вы хотите данные путем агрегации.

3 голосов
/ 06 октября 2010

Ваш вопрос не четко определен - когда вы говорите, что вам нужны также другие данные из той же строки, вы не определяете, какая строка.

Вы говорите, что вам нужно упорядочить результаты по created_at, поэтомуЯ предположу , что вы хотите, чтобы значения из строки имели минимальное значение created_at (самое раннее).

Теперь это становится одним из наиболее распространенных вопросов SQL - получение строк, содержащих какое-либо статистическое значение (MIN, MAX).

Например,

SELECT user_id, MIN(created_at) AS created_at
FROM creations
GROUP BY user_id
ORDER BY MIN(create_at)
LIMIT 20

Этот подход не позволит вам (легко) выбрать другие значения из той же строки.

Один подход, который позволитВы выбираете другие значения

SELECT c.user_id, c.created_at, c.other_columns
FROM creations c LEFT JOIN creation c_help
     ON c.user_id = c_help.user_id AND c.created_at > c_help.create_at
WHERE c_help IS NULL
ORDER BY c.created_at
LIMIT 20
3 голосов
/ 06 октября 2010

GROUP BY должен обеспечивать отличные значения сгруппированных столбцов, это может дать вам то, что вы ищете.

(Обратите внимание, я вкладываю свои 2 цента, хотя я не знаком с PostgreSQL,а скорее MySQL и Oracle)

В MySql

SELECT user_id, created_at
FROM creations
GROUP BY user_id
ORDER BY user_id

В Oracle sqlplus

SELECT user_id, FIRST(created_at)
FROM creations
GROUP BY user_id
ORDER BY user_id

Это даст вам user_id, за которым следует сначала created_at связанный с этим user_id.Если вам нужен другой created_at, у вас есть возможность заменить FIRST другими функциями, такими как AVG, MIN, MAX или LAST в Oracle, вы также можете попробовать добавить ORDER BY в другие столбцы (включая те, которые не возвращены, чтобы дать вам created_at.

2 голосов
/ 06 октября 2010

Кто-то предложил использовать подзапрос на канале irc #postgresql. Сработало:

SELECT user_id  
FROM (SELECT DISTINCT ON (user_id) * FROM creations) ss  
ORDER BY created_at DESC  
LIMIT 20;
...