使用DISTINCT子句过滤数据但仍然拉出其他不是DISTINCT的字段

时间:2010-10-05 22:07:15

标签: sql ruby-on-rails postgresql distinct

我正在尝试在Postgresql中编写一个查询,该查询提取一组有序数据并按不同的字段对其进行过滤。我还需要从同一个表行中提取其他几个字段,但是它们需要被排除在不同的评估之外。例如:

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

我需要user_idDISTINCT,但不关心created_at日期是否唯一。由于created_at日期包含在评估中,因此我的结果集中出现了重复的user_id

此外,数据必须按日期排序,因此此处不能使用DISTINCT ON。它要求DISTINCT ON字段是ORDER BY子句中的第一个字段,并且不会提供我所寻求的结果。

如何正确使用DISTINCT子句,但在选择其他字段时仅将其范围限制为一个字段?

5 个答案:

答案 0 :(得分:5)

正如您所发现的那样,标准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上的手册。

答案 1 :(得分:4)

如果你想为每个用户提供最新的created_at,那么我建议你这样聚合:

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

这将返回每个user_id的最新created_at 如果你只想要前20名,那么追加

LIMIT 20

编辑:这与Unreason上面说的基本相同......通过聚合定义您想要数据的行。

答案 2 :(得分:3)

GROUP BY应该确保分组列的不同值,这可能会为您提供所需的内容。

(请注意,即使我不熟悉PostgreSQL,我也只能投入2美分,而不是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替换为Oracle中的AVGMINMAXLAST等其他函数,还尝试在其他列(包括未返回的列)上添加ORDER BY,以便为您提供不同的created_at

答案 3 :(得分:3)

您的问题没有明确定义 - 当您说您还需要来自同一行的其他数据时,您没有定义哪一行。

您确实说您需要按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

答案 4 :(得分:2)

有人在irc #postgresql频道上建议使用子查询。它奏效了:

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