【问题标题】:How to use DISTINCT and ORDER BY in same SELECT statement?如何在同一个 SELECT 语句中使用 DISTINCT 和 ORDER BY?
【发布时间】:2011-07-20 11:29:44
【问题描述】:

执行以下语句后:

SELECT  Category  FROM MonitoringJob ORDER BY CreationDate DESC

我正在从数据库中获取以下值:

test3
test3
bildung
test4
test3
test2
test1

但我希望删除重复项,如下所示:

bildung
test4
test3
test2
test1

我尝试使用 DISTINCT,但它不适用于 ORDER BY 在一个语句中。请帮忙。

重要:

  1. 我试过了:

    SELECT DISTINCT Category FROM MonitoringJob ORDER BY CreationDate DESC
    

    它不起作用。

  2. CreationDate 的顺序非常重要。

【问题讨论】:

  • 怎么不行?输出错误?
  • 如果你使用postgres,显然你可以SELECT DISTINCT ON (value), * FROM table ORDER BY value, and_the_value_you_want_to_order_from DESC

标签: sql sql-order-by distinct


【解决方案1】:

问题在于ORDER BY 中使用的列未在DISTINCT 中指定。为此,您需要使用aggregate function 进行排序,并使用GROUP BY 使DISTINCT 工作。

试试这样的:

SELECT DISTINCT Category, MAX(CreationDate) 
FROM MonitoringJob 
GROUP BY Category 
ORDER BY MAX(CreationDate) DESC, Category

【讨论】:

  • 如果您按类别分组,您甚至不需要 DISTINCT 关键字。
  • 为了解释为什么你需要一个聚合函数(在你的 ORDER BY 中,不一定在你的 SELECT 中!),我找到了一篇简洁的文章:weblogs.sqlteam.com/jeffs/2007/12/13/… TLDR: When using MIN() or MAX() 对 GROUP BY 之后的每一行,它抓取组中的最大值或最小值。
【解决方案2】:

扩展的排序键列

您想要做的事情不起作用的原因是因为logical order of operations in SQL,对于您的第一个查询,它是(简化的):

  • FROM MonitoringJob
  • SELECT Category, CreationDate 即添加一个所谓的扩展排序键列
  • ORDER BY CreationDate DESC
  • SELECT Category 即再次从结果中删除扩展排序键列

因此,由于 SQL 标准的扩展排序键列 功能,完全可以按不在 SELECT 子句中的内容进行排序,因为它是在后面临时添加的场景。

那么,为什么这不适用于DISTINCT

如果我们添加DISTINCT 操作,它将添加在SELECTORDER BY 之间:

  • FROM MonitoringJob
  • SELECT Category, CreationDate
  • DISTINCT
  • ORDER BY CreationDate DESC
  • SELECT Category

但是现在,有了扩展的排序键列 CreationDateDISTINCT 操作的语义已经改变,因此结果将不再相同。这不是我们想要的,因此 SQL 标准和所有合理的数据库都禁止这种用法。

解决方法

可以用标准语法模拟如下

SELECT Category
FROM (
  SELECT Category, MAX(CreationDate) AS CreationDate
  FROM MonitoringJob
  GROUP BY Category
) t
ORDER BY CreationDate DESC

或者,只是简单地(在这种情况下),如 Prutswonder 所示

SELECT Category, MAX(CreationDate) AS CreationDate
FROM MonitoringJob
GROUP BY Category
ORDER BY CreationDate DESC

I have blogged about SQL DISTINCT and ORDER BY more in detail here.

【讨论】:

  • 我认为您误解了DISTINCT ON 的工作原理,并且很确定它在这里没有帮助。括号中的表达式用于确定区别(分组条件)。如果有不同的类别具有相同的CreationDate,那么结果中只会出现其中一个!因为我想知道我是不是错了,所以我还在您的博客文章中加载了示例数据库以进行仔细检查:您在那里提供的 DISTINCT ON 查询总共产生了 1000 个结果(有大量重复的 lengths)而它下面的查询只给出了 140 个(唯一)值。
  • @Inkling:感谢您的宝贵时间。 OP 明确希望删除“重复项”。请参阅 OP 的措辞 “但我希望删除重复项,就像这样”。从我的博客文章中复制查询时,您可能犯了一个错误。有两种查询,一种使用DISTINCT(没有ON),另一种使用DISTINCT ON。请注意,后者明确不会删除重复的长度,而是删除重复的标题。我确实认为我在这里的回答是完全正确的。
  • 我的意思是您的DISTINCT ON 条件正在使用错误的条件删除重复项。在您的博客文章中,DISTINCT ON 查询确实删除了重复的 titles,但是它上面的 DISTINCT 查询和它下面的查询(您声称它是“语法糖”)都删除了重复的长度,因为这大概是整个目标。这里同样适用:OP 希望删除重复的 Categories,而不是像 DISTINCT ON 查询那样重复的 CreationDates。如果你仍然不相信我,你自己测试一下。
【解决方案3】:

如果不需要 MAX(CreationDate) 的输出 - 就像在原始问题的示例中一样 - 唯一的答案是 Prashant Gupta 答案的第二个陈述:

SELECT [Category] FROM [MonitoringJob] 
GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC

说明:您不能在内联函数中使用 ORDER BY 子句,因此 Prutswonder 答案中的语句在这种情况下不可用,您不能在其周围放置外部选择并丢弃 MAX(CreationDate ) 部分。

【讨论】:

    【解决方案4】:

    只需使用此代码,如果您想要 [Category] ​​和 [CreationDate] 列的值

    SELECT [Category], MAX([CreationDate]) FROM [MonitoringJob] 
                 GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC
    

    或使用此代码,如果您只想要 [Category] ​​列的值。

    SELECT [Category] FROM [MonitoringJob] 
    GROUP BY [Category] ORDER BY MAX([CreationDate]) DESC
    

    您将拥有所有您想要的不同记录。

    【讨论】:

    • 那些大括号 [] 完全令人困惑......这是有效的 SQL 语法吗?
    • 括号用于转义关键字,例如订单、事件等,因此如果您的表中有(例如)名为Event 的列,您可以写[Event] 而不是@987654325 @ 停止 SQL 引发解析错误。
    【解决方案5】:

    2) 按 CreationDate 排序很重要

    原来的结果表明“test3”有多个结果...

    很容易开始一直使用 MAX 来删除 Group By 中的重复项……忘记或忽略根本问题是什么……

    OP 大概意识到使用 MAX 会给他最后一个“创建”,而使用 MIN 会给出第一个“创建”...

    【讨论】:

    • 这似乎并没有真正回答这个问题,它似乎是对其他回答者使用MAX 的评论,而不是作为对问题的独立答案的评论。跨度>
    【解决方案6】:
    if object_id ('tempdb..#tempreport') is not null
    begin  
    drop table #tempreport
    end 
    create table #tempreport (
    Category  nvarchar(510),
    CreationDate smallint )
    insert into #tempreport 
    select distinct Category from MonitoringJob (nolock) 
    select * from #tempreport  ORDER BY CreationDate DESC
    

    【讨论】:

      【解决方案7】:

      Distinct 将按升序对记录进行排序。如果要按 desc 顺序排序,请使用:

      SELECT DISTINCT Category
      FROM MonitoringJob
      ORDER BY Category DESC
      

      如果要根据 CreationDate 字段对记录进行排序,则此字段必须在 select 语句中:

      SELECT DISTINCT Category, creationDate
      FROM MonitoringJob
      ORDER BY CreationDate DESC
      

      【讨论】:

      • 这将执行但不会提供 OP 所需的内容。 OP 想要不同的类别,而不是类别和 CreateDate 的不同组合。此代码可以生成同一类别的多个实例,每个实例具有不同的 CreationDate 值。
      【解决方案8】:

      您可以使用 CTE:

      WITH DistinctMonitoringJob AS (
          SELECT DISTINCT Category Distinct_Category FROM MonitoringJob 
      )
      
      SELECT Distinct_Category 
      FROM DistinctMonitoringJob 
      ORDER BY Distinct_Category DESC
      

      【讨论】:

        【解决方案9】:

        通过子查询,它应该可以工作:

            SELECT distinct(Category) from MonitoringJob  where Category in(select Category from MonitoringJob order by CreationDate desc);
        

        【讨论】:

        • 嗯...我认为不会。外部选择未排序。
        • 这行不通,我在这里是因为这行不通
        • 一点帮助都没有!
        【解决方案10】:

        下一个试试,但是对大数据没用...

        SELECT DISTINCT Cat FROM (
          SELECT Category as Cat FROM MonitoringJob ORDER BY CreationDate DESC
        );
        

        【讨论】:

        • "ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效,除非还指定了 TOP 或 FOR XML。"
        • 这不起作用,因为您没有在 order by 上指定列 CreationDate。
        • @TechplexEngineer 您的评论不正确。在子查询中使用ORDER BY 是绝对有效的。甚至有人对你的错误评论投了赞成票。
        • 我正在尝试这个并且与@TechplexEngineer 有同样的错误。我正在使用带有 case when 的自定义排序。
        【解决方案11】:

        可以像这样使用内部查询来完成

        $query = "SELECT * 
                    FROM (SELECT Category  
                        FROM currency_rates                 
                        ORDER BY id DESC) as rows               
                    GROUP BY currency";
        

        【讨论】:

          【解决方案12】:
          SELECT DISTINCT Category FROM MonitoringJob ORDER BY Category ASC
          

          【讨论】:

          • 我需要按创建日期排序!!这很重要
          • 那么是不是不能自己添加要订购的栏目呢?您的示例显示按字母顺序排列的条目。如果您需要按创建日期排序,只需添加它。真的没那么难。
          • -1 :OP 尝试过,但没有成功,因为这是不可能的,而且您在光顾 OP 时显然忽略了这一事实。关键是 DISTINCT 运算符将整理多个具有相同类别值的记录,每个记录可能具有不同的创建日期。因此,使用 DISTINCT 在逻辑上是不可能的。这会将所需的逻辑推送到 GROUP BY 而不是 DISTINCT,从而允许在创建日期进行聚合 (MAX)。
          • 实际上,如果您仔细看看 OP 做了什么,那绝对是格式错误的 SQL - 我没有犯任何错误,并且给出的结果与他要求的结果相对应。我不会费心-1,下次纠正人之前阅读。谢谢。
          • 你直接建议添加CreationDate字段,甚至说“真的没那么难”。这样做会产生格式错误的 SQL。您因光顾 OP、提供建议以使 OP 回到他最初发布的声明,并且没有注意到 DISTINCT 和按不在 DISTINCT 中的字段排序之间的争用而得到 -1。此外,“b”在“t”之前,“1”在“4”之前,因此 OP 给出的结果绝对不是按字母顺序排列的。那么我可以提出你自己的建议吗:下次阅读(更仔细地)。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-21
          • 1970-01-01
          • 1970-01-01
          • 2012-02-20
          • 1970-01-01
          相关资源
          最近更新 更多