我正在尝试在Postgresql中编写一个查询,该查询提取一组有序数据并按不同的字段对其进行过滤。我还需要从同一个表行中提取其他几个字段,但是它们需要被排除在不同的评估之外。例如:
SELECT DISTINCT(user_id) user_id,
created_at
FROM creations
ORDER BY created_at
LIMIT 20
我需要user_id
为DISTINCT
,但不关心created_at日期是否唯一。由于created_at日期包含在评估中,因此我的结果集中出现了重复的user_id
。
此外,数据必须按日期排序,因此此处不能使用DISTINCT ON
。它要求DISTINCT ON
字段是ORDER BY
子句中的第一个字段,并且不会提供我所寻求的结果。
如何正确使用DISTINCT
子句,但在选择其他字段时仅将其范围限制为一个字段?
答案 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中的AVG
,MIN
,MAX
或LAST
等其他函数,还尝试在其他列(包括未返回的列)上添加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;