8

根据另一篇 SO 帖子(SQL:如何使用 DISTINCT 保持行顺序?),就排序而言,不同的行为非常未定义。

我有一个查询:

select col_1 from table order by col_2

这可以返回像

3
5
3
2

然后我需要在这些上选择一个不同的保留顺序,这意味着我想要

select distinct(col_1) from table order by col_2 

返回

3
5
2

但不是

5
3
2

这是我真正想要做的。Col_1 是用户 ID,col_2 是该用户的登录时间戳事件。所以同一个用户(col_1)可以有很多登录时间。我正在尝试建立在系统中看到他们的用户的历史列表。我想说“我们的第一个用户曾经是,我们的第二个用户曾经是”,等等。

那篇文章似乎建议使用 group by,但 group by 并不意味着返回行的顺序,所以我不明白这将如何或为什么适用于此,因为它不会出现 group by 将保留任何顺序。事实上,另一篇 SO 帖子给出了一个示例,其中 group by 将破坏我正在寻找的排序:请参阅“Peter”,了解 sql 中 GROUP BY 和 ORDER BY 之间的区别。有没有办法保证后一种结果?奇怪的是,如果在实现 DISTINCT 子句,我肯定会先做 order by,然后获取结果并对列表进行线性扫描并自然保留排序,所以我不确定为什么会这样不明确的。

编辑:

谢谢你们!我接受了 IMSoP 的回答,因为不仅有一个我可以玩的交互式示例(感谢您让我开始使用 SQL Fiddle),而且他们还解释了为什么有几件事以他们的工作方式工作,而不是简单地“这样做” . 具体来说,尚不清楚 GROUP BY 不会破坏(而是将它们保存在某种内部列表中)group by 之外的其他列中的值,并且这些值仍然可以在 ORDER BY 子句中检查。

4

4 回答 4

12

这一切都与 SQL 语句的“逻辑顺序”有关。尽管 DBMS 实际上可能会根据各种巧妙的策略检索数据,但它必须根据一些可预测的逻辑来运行。因此,就逻辑的行为方式而言,SQL 查询的不同部分可以被认为是“先于”或“后”处理的。

碰巧的是,该ORDER BY子句是该逻辑序列中的最后一步,因此它不能改变“早期”步骤的行为。

如果您使用 a GROUP BY,则在运行子句时,行已被捆绑到它们的组中SELECT,更不用说 了ORDER BY,因此您只能查看已分组的列,或“聚合”在所有值中计算的值一组。(MySQL 实现了一个有争议的扩展GROUP BY,您可以在其中提及SELECT逻辑上不存在的列,它将从该组中的任意行中选择一个)。

如果您使用 a DISTINCT,它会在 之后进行逻辑处理SELECT之后ORDER BY仍然会出现。因此,只有在DISTINCT丢弃重复项后,剩余的结果才会按特定顺序排列 - 但已丢弃的行不能用于确定该顺序。


至于如何得到你需要的结果,关键是找到一个在/ (逻辑上)运行后有效的值进行排序。请记住,如果您使用 a ,任何聚合值仍然有效 - 聚合函数可以查看组中的所有值。这包括和,它们非常适合排序,因为“最小数字” ( ) 与“如果我按升序对它们进行排序时的第一个数字”是相同的,反之亦然。GROUP BYDISTINCTGROUP BYMIN()MAX()MINMAX

foo_number因此,要根据适用于每个值的最低值订购一组不同的值bar_number,您可以使用以下命令:

SELECT foo_number
FROM some_table
GROUP BY foo_number
ORDER BY MIN(bar_number) ASC

这是一个带有一些任意数据的现场演示


编辑:在评论中,讨论了为什么如果在分组/重复数据删除发生之前应用排序,则该顺序不适用于组。如果是这种情况,您仍然需要一个策略,以便在每个组中保留哪一行:第一行或最后一行。

打个比方,将原始行组想象成从一副牌中挑选的一组扑克牌,然后按面值从低到高排序。现在浏览已分类的牌组,并将它们分成单独的一堆,用于每套西装。哪张牌应该“代表”每一堆?

如果您将牌面朝上发牌,最后出现的牌将是面值最高的牌 “保持最后”策略);如果您将它们面朝下处理然后翻转每一堆,您将显示最低的面值(“保持第一”策略)。两者都遵循卡片的原始顺序,并且“根据花色发牌”的指令不会自动告诉庄家(代表 DBMS)打算采用哪种策略。

如果最后一堆卡片是 a 中的组GROUP BY,则MIN()MAX()代表拿起每一堆并寻找最低或最高值,无论它们的顺序如何。但是因为您可以查看组内部,所以您可以做其他事情同样,例如将每堆的总价值(SUM)或有多少张牌(COUNT)等相加,这GROUP BY比“有序DISTINCT”要强大得多。

于 2013-10-16T22:01:59.653 回答
1

引用的GROUP BY答案中的 不是试图执行排序......它只是为我们想要区分的列选择一个关联值。

像@bluefeet 状态一样,如果您想要有保证的订购,您必须使用ORDER BY.

为什么我们不能在 中指定一个ORDER BY不包含在 中的值SELECT DISTINCT

col1考虑和的以下值col2

create table yourTable (
  col_1 int,
  col_2 int
);

insert into yourTable (col_1, col_2) values (1, 1);
insert into yourTable (col_1, col_2) values (1, 3);
insert into yourTable (col_1, col_2) values (2, 2);
insert into yourTable (col_1, col_2) values (2, 4);

有了这些数据,应该SELECT DISTINCT col_1 FROM yourTable ORDER BY col_2返回什么?

这就是为什么你需要GROUP BY和聚合函数来决定col_2你应该对多个值中的哪一个进行排序...可能是MIN(),可能是MAX(),甚至可能是其他一些函数,例如AVG()在某些情况下是有意义的;这一切都取决于具体的场景,这就是为什么你需要明确:

select col_1
from yourTable
group by col_1
order by min(col_2)

SQL Fiddle Here

于 2013-10-16T22:20:13.750 回答
1

我会去像

select col1
from (
select col1,
       rank () over(order by col2) pos
from table
)
group by col1
order by min(pos)

在子查询中我计算位置,然后在主查询中我对 col1 进行分组,使用最小的位置进行排序。

这里是SQLFiddle 中的演示(这是 Oracle,MySql 信息是后来添加的。

为 MySql 编辑:

select col1
from (
select col1 col1,
       @curRank := @curRank + 1 AS pos
from table1, (select @curRank := 0) p
) sub
group by col1
order by min(pos)

这里是 MySql 的演示

于 2013-10-16T21:54:19.527 回答
0

仅对于 MySQL,当您选择不在 GROUP BY 中的列时,它将从组中的第一条记录返回列。您可以使用此行为来选择从每个组返回的记录,如下所示:

SELECT foo_number, bar_number
FROM 
(
  SELECT foo_number, bar_number 
  FROM some_table 
  ORDER BY bar_number
) AS t
GROUP BY foo_number
ORDER BY bar_number DESC;

这更加灵活,因为它允许您使用聚合无法使用的表达式对每个组中的记录进行排序 - 在我的情况下,我想在另一列中返回具有最短字符串的记录。

为了完整起见,我的查询如下所示:

SELECT
  s.NamespaceId,
  s.Symbol,
  s.EntityName
FROM 
(
  SELECT 
    m.NamespaceId,
    i.Symbol, 
    i.EntityName
  FROM ImportedSymbols i
  JOIN ExchangeMappings m ON i.ExchangeMappingId = m.ExchangeMappingId
  WHERE
    i.Symbol NOT IN 
    (
      SELECT Symbol 
      FROM tmp_EntityNames
      WHERE NamespaceId = m.NamespaceId
    )
      AND
    i.EntityName IS NOT NULL
  ORDER BY LENGTH(i.RawSymbol), i.RawSymbol
) AS s
GROUP BY s.NamespaceId, s.Symbol;

这样做是在每个命名空间中返回一个不同的符号列表,对于重复的符号,返回具有最短 RawSymbol 的符号。当 RawSymbol 长度相同时,它会返回 RawSymbol 按字母顺序排在第一位的那个。

于 2015-12-16T23:08:22.510 回答