SQL选择不同但“先保留”?

时间:2013-10-16 21:42:59

标签: mysql sql

根据另一篇SO帖子(SQL: How to keep rows order with DISTINCT?),就排序而言,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 by will任何订购。事实上,另一个SO帖子给出了一个例子,其中group by将破坏我正在寻找的顺序:见what is the difference between GROUP BY and ORDER BY in sql中的“Peter”。无论如何要保证后者的结果吗?奇怪的是,如果正在实现DISTINCT子句,我肯定会先执行命令,然后取结果并对列表进行线性扫描并自然保留顺序,所以我是不确定为什么行为如此不确定。

编辑:

谢谢大家!我接受了IMSoP的答案,因为不仅有一个我可以使用的交互式示例(感谢我转向SQL Fiddle),但他们也解释了为什么有些事情按照他们的工作方式工作,而不仅仅是“做这个” 。具体来说,不清楚GROUP BY不会在组外的其他列中销毁(而是将它们保留在某种内部列表中)值,并且仍然可以在ORDER BY子句中检查这些值。

4 个答案:

答案 0 :(得分:10)

这一切都与SQL语句的“逻辑排序”有关。虽然DBMS可能实际上根据各种聪明的策略检索数据,但它必须根据某些可预测的逻辑来运行。因此,根据逻辑的行为方式,可以认为SQL查询的不同部分在“之前”或“之后”处理。

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

如果你使用GROUP BY,那么在运行SELECT子句时行已经捆绑到他们的组中,更不用说ORDER BY了,所以你只能看一下已按组中的所有值计算的“聚合”值或“聚合”值的列。 (MySQL实现了a controversial extension to GROUP BY,你可以在SELECT中提到一个逻辑不存在的列,它将从该组中的任意行中选择一个。)

如果您使用DISTINCT,则会在<{em> SELECT之后对其进行逻辑处理,但ORDER BY之后仍会出现。因此,只有当DISTINCT抛弃重复项时,剩余的结果才会被置于特定的顺序中 - 但已丢弃的行不能用于确定该顺序。


至于如何获得所需的结果,关键是在GROUP BY / DISTINCT具有(逻辑上)之后找到一个有效的排序值已经运行了。请记住,如果使用GROUP BY,则任何聚合值仍然有效 - 聚合函数可以查看组中的所有值。这包括MIN()MAX(),它们是排序的理想选择,因为“最低数字”(MIN)与“第一个数字相同”,如果我按升序对它们进行排序“,反之亦然MAX

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

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

Here's a live demo with some arbitrary data


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

作为类比,将原始行集描绘成从牌组中挑选的一组扑克牌,然后按其面值从低到高排序。现在,穿过分类的甲板,将它们分成一堆,分别用于每件套装。哪张卡应该“代表”每一堆?

如果您面朝上交易,最后显示的卡片将是最高面值的卡片(“保持最后”策略);如果你面朝下处理它们然后翻转每一堆,你将揭示最低面值(一个“先保留”策略)。两者都遵守卡片的原始顺序,并且“基于套装处理卡片”的说明并不会自动告诉经销商(代表DBMS)策略的目的。

如果最后一堆卡片来自GROUP BY,则MIN()MAX()代表拾取每一堆并查找最低或最高值,无论顺序如何但是因为你可以查看这些组,你也可以做其他的事情,比如加上每一堆的总价值(SUM)或者有多少张牌(COUNT)等等。 ,使GROUP BY比“有序的DISTINCT”强大得多。

答案 1 :(得分:1)

我会选择像

这样的东西
select col1
from (
select col1,
       rank () over(order by col2) pos
from table
)
group by col1
order by min(pos)

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

这里是demo in 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)

这里the demo for MySql

答案 2 :(得分:1)

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

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

为什么我们无法在ORDER BY中指定未包含在SELECT DISTINCT中的值?

考虑col1col2的以下值:

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(),可以是{{ 1}},甚至某些其他函数如MAX()在某些情况下也会有意义;这一切都取决于具体的情况,这就是你需要明确的原因:

AVG()

SQL Fiddle Here

答案 3 :(得分: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首先按字母顺序排列的那个。