【问题标题】:PostgreSQL does not allow me to group a column with orderPostgreSQL 不允许我按顺序对列进行分组
【发布时间】:2012-04-26 22:23:04
【问题描述】:

在 PostgreSQL 中,我想一次获取每个用户并按日期排序。

这是我的查询:

SELECT id, useridx, isread, message, date
  FROM messages
 WHERE isread = 1
 GROUP BY useridx
 ORDER BY date DESC

这是一个样本数据

------------------------------------------------------
+  id  |  useridx |  isread  |  messsage |  date     +
------------------------------------------------------
   1   |  1       |  0        | Hello    |  2012-01-01    
   2   |  2       |  1        | Hi       |  2012-01-02    
   3   |  3       |  1        | Test     |  2012-01-03    
   4   |  3       |  0        | My Msg   |  2012-01-04    
   5   |  4       |  1        | sadasd   |  2012-01-05    
   6   |  4       |  1        | sdfsdfd  |  2012-01-06    
   7   |  4       |  0        | sdfsdfsd |  2012-01-07    
   8   |  5       |  0        | 5345634  |  2012-01-08
   9   |  6       |  0        | sdfdfsd  |  2012-01-09
   10  |  7       |  0        | sdfsdfsf |  2012-01-10
------------------------------------------------------

现在,我要做的是通过 useridx 对它们进行分组并按日期排序来获取此表。

预期结果

------------------------------------------------------
+  id  |  useridx |  isread  |  messsage |  date     +
------------------------------------------------------  
   6   |  4       |  1        | sdfsdfd  |  2012-01-06 
   3   |  3       |  1        | Test     |  2012-01-03  
   2   |  2       |  1        | Hi       |  2012-01-02    
------------------------------------------------------

实际结果

ERROR:  column "messages.date" must appear in the GROUP BY clause or be used in an aggregate function

我也不想分组日期。我只想用 useridx 分组并按日期 DESC 对它们进行排序。

感谢任何帮助/想法!

注意:我也试过 Distinct。不符合我的需要或我做错了。

我在DISTINCT ONrank() 方法之间非常困惑和卡住

结论: 对于在这里遇到相同问题的人,可以将其作为答案阅读。 @kgrittn 和 @mu 都太短了,答案都是正确的。我将继续在我的项目中使用答案和模式,并且及时我可以理解哪个是最好的-我猜-。因此,选择其中之一并继续您的工作。你会没事的。

上次更新:有时,Distinct On 会从结果中排除某些 id。假设我有一个 id 列,我有 6 行是相同的。因此,从结果中排除它是不同的,但 rank() 只是结果。 所以,使用 rank()!

【问题讨论】:

  • 如果您觉得他们需要总结一个完整的解决方案,或者如果有任何答案可以改进,您可以提出修改建议。

标签: postgresql group-by distinct distinct-on


【解决方案1】:

与 MySQL 不同,PostgreSQL 不会为未在聚合查询中聚合的列显示随机数据。

解决方法在错误信息中

ERROR:  column "messages.date" must appear in the GROUP BY clause or be used in an aggregate function

这意味着您必须在选择此列时对“messages.date”列进行 GROUP BY 或使用 MIN() 或 MAX() 等聚合函数

例子:

SELECT MIN(id), useridx, isread, message, MAX(date)
FROM messages WHERE isread = 1 
GROUP BY useridx, isread, message
ORDER BY MAX(date) DESC

【讨论】:

  • 我现在很困惑。您的解决方案也有效。哪一个是正确的方法?你的还是 vyegorov 的?
  • 这取决于您要取回哪些数据。例如,查看 id 为 3 和 4 的行,它们都有 useridx 3。当您按 useridx 进行 GROUP 时,您必须告诉 postgres 您想要哪个 id 值...
  • 我现在意识到,当我在大表中运行您的查询时,它有时会显示两次 useridx,因为消息不同?
【解决方案2】:

您想使用rank() window function 对每个useridx 组中的结果进行排序,然后通过将排名结果包装在派生表中来剥离第一个:

select id, useridx, isread, message, date
from (
    select id, useridx, isread, message, date,
           rank() over (partition by useridx order by date desc) as r
    from messages
    where isread = 1
) as dt
where r = 1

这将为您的示例提供id 2、3 和 6 的行。您可能希望在 over 中添加辅助排序键,以便在同一日期每个 useridx 有多个消息时始终做出选择。

您至少需要 PostgreSQL 8.4 (AFAIK) 才能拥有窗口函数。

【讨论】:

  • 天哪。这也很有效。我真的很困惑我应该选择哪个答案......
  • @GencerGenç:我当然愿意 :) 这正是窗口函数所适用的那种情况。如果用户在一个日期有多条消息,vyegorov 会出现问题,ilanco 会生成不在表中的行;这些是解决这些问题的非窗口函数方法的常见问题。
  • 谢谢!我刚刚完成了这个。我要做的最后一件事是将此代码应用于 Zend_Db。答案被接受。
  • 等一下。如果我想添加多个 WHERE 语句怎么办? r=1 可能无法正常工作?
  • @GencerGenç:我不确定你的意思。您想在isread = 1 周围添加更多条件,还是希望每个用户获得更多结果?
【解决方案3】:

另一种选择是使用SELECT DISTINCT ON(这与简单的SELECT DISTINCT 非常不同):

SELECT *
  FROM (SELECT DISTINCT ON (useridx)
            id, useridx, isread, message, date
          FROM messages
          WHERE isread = 1
          ORDER BY useridx, date DESC) x
  ORDER BY date DESC;

在某些情况下,这可以比其他方法更好地扩展。

【讨论】:

  • 嗯。我现在真的很困惑!这也很有效。 rank() 和这都给了我大桌子的确切结果。那么,现在哪一种方法是正确的呢? rank()DISTINCT ON。如果可能的话,我想讨论这个。
  • 对两个查询运行 EXECUTE ANALYZE ... 并比较结果。
  • @vyegorov 我对DISTINCT ONrank() 都做了EXPLAIN ANALYZE,我只能说它们几乎是一样的。几行之后完全一样。两者的总运行时间约为 3.474 毫秒,执行时间约为 0.077 毫秒。所以,还在想用什么方法……
  • 那么大表是什么意思? messages有多少条记录?
  • 如果两者都表现良好并给出正确的结果,你应该选择你觉得更容易理解的那个。从长远来看,这将降低维护成本。
【解决方案4】:

多年后,但您不能只在 FROM 子查询中订购:

SELECT m.id, m.useridx, m.isread, m.message, m.date
FROM (
   SELECT m2.id, m2.useridx, m2.isread, m2.message, m2.date 
   FROM message m2 
   ORDER BY m2.id ASC, m2.date DESC
) m
WHERE isread = 1
GROUP BY useridx

这在 PostgreSQL 9.2 中适用于我

【讨论】:

    【解决方案5】:

    您正在汇总结果。

    这意味着用户3 将只有一行,而不是两行。但您也可以为聚合行选择 idmessageisread 列。 PostgreSQL 应该如何传递这些数据?应该是max() 的可能值吗?也许min()

    我假设您希望获得有关最新消息的数据。试试这个查询:

    SELECT id, useridx, isread, message, date FROM messages
     WHERE isread = 1 AND (useridx, date) IN
      (SELECT useridx, max(date) FROM messages WHERE isread = 1 GROUP BY useridx);
    

    【讨论】:

    • 感谢您的快速回复,但我应用了您的样品并且仍然混合。我无法按日期对它们进行排序。 :(
    • 嗯。现在,我在查询末尾添加了 ORDER BY date DESC 并开始工作。如果它不正确,请告诉我。
    • 如果您在同一日期为一个useridx 发送多条消息会怎样?
    • 您的解决方案也有效!我在一个大型数据库中进行了测试,并且工作起来非常有魅力!
    • @muistooshort 说得对,因为我在多条消息落在同一日期的情况下容易受到攻击。是的,它在这种特殊情况下有效,但他的 rank() 解决方案是正确的。
    猜你喜欢
    • 2017-12-22
    • 1970-01-01
    • 2017-06-11
    • 2019-12-20
    • 2021-12-12
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多