【问题标题】:MySQL ORDER BY Column = value AND distinct?MySQL ORDER BY Column = value AND distinct?
【发布时间】:2014-06-24 14:13:42
【问题描述】:

我现在头发都变白了……

我有一张这样的桌子。

ID - Place - Person
1 - London - Anna
2 - Stockholm - Johan
3 - Gothenburg - Anna
4 - London - Nils

我想得到包含所有不同人的结果,但我想选择订购的地点。

例如。我想得到一个列表,它们是由 LONDON 订购的,其余的将随之而来,但在 PERSON 上是不同的。

Output like this:
ID - Place - Person
1 - London - Anna
4 - London - Nils
2 - Stockholm - Johan

试过这个:

SELECT ID, Person
FROM users        
ORDER BY FIELD(Place,'London'), Person ASC "

但它给了我:

ID - Place - Person
1 - London - Anna
4 - London - Nils
3 - Gothenburg - Anna
2 - Stockholm - Johan

而且我真的不希望 Anna 或任何人多次出现在结果中。

【问题讨论】:

  • +1。这是一个看似不平凡的规范,每个Person 只返回一行;确保该行是'Place='London' 行(如果存在),否则为其他行。一个规范看起来很简单,但实际实现会让你白发苍苍!不幸的是,MySQL 没有像其他 RDBMS 那样的分析函数,但一种选择是模拟“ROW_NUMBER”分析函数。执行此操作的 SQL 不是很直接。 (有关该方法的示例,请参见我的答案。)
  • “伦敦 - 安娜”可以出现多次吗?
  • @Strawberry 不,整个想法是只让一个人先按一个地方对它们进行排序。然后其他人将按照 ASC 顺序跟随。
  • 我的意思是在数据集中。不是结果集。
  • @Strawberry 哦。我的错。 “伦敦 - 安娜”只会出现一次。

标签: mysql sql asp-classic field sql-order-by


【解决方案1】:

这是获取指定输出的一种方法,但它使用 MySQL 特定的行为,这是不能保证的:

SELECT q.ID
     , q.Place
     , q.Person
  FROM ( SELECT IF(p.Person<=>@prev_person,0,1) AS r
              , @prev_person := p.Person AS person
              , p.Place
              , p.ID
           FROM users p
          CROSS
           JOIN (SELECT @prev_person := NULL) i  
          ORDER BY p.Person, !(p.Place<=>'London'), p.ID
       ) q
 WHERE q.r = 1
ORDER BY !(q.Place<=>'London'), q.Person

此查询使用内联视图以特定顺序返回所有行,按 Person,以便所有 'Anna' 行都在一起,然后是所有 'Johan' 行,等等。每个人都按Place='London' 排序,然后按 ID。

“诀窍”是使用 MySQL 用户变量将当前行的值与前一行的值进行比较。在此示例中,我们正在检查当前行中的“Person”是否与前一行中的“Person”相同。根据该检查,如果这是我们为一个人处理的“第一”行,则返回 1,否则返回 0。

最外面的查询处理内联视图中的行,并排除每个人的“第一”行(我们从内联视图返回的 0 或 1)以外的所有行。

(这不是获得结果集的唯一方法。但这是模拟其他 RDBMS 中可用的分析函数的一种方法。)


为了比较,在 MySQL 以外的其他数据库中,我们可以使用类似这样的 SQL:

SELECT ROW_NUMBER() OVER (PARTITION BY t.Person ORDER BY 
         CASE WHEN t.Place='London' THEN 0 ELSE 1 END, t.ID) AS rn
      , t.ID
      , t.Place
      , t.Person
   FROM users t
  WHERE rn=1
  ORDER BY CASE WHEN t.Place='London' THEN 0 ELSE 1 END, t.Person

跟进

在答案的开头,我提到了无法保证的 MySQL 行为。我指的是在 SQL 语句中使用 MySQL 用户定义变量。

摘自 MySQL 5.5 参考手册http://dev.mysql.com/doc/refman/5.5/en/user-variables.html

“作为一般规则,除了在 SET 语句中,您不应该为用户变量赋值并在同一语句中读取值。”

“对于其他语句,例如 SELECT,您可能会得到预期的结果,但这不能保证。”

“涉及用户变量的表达式的求值顺序未定义。”

【讨论】:

  • 这解决了问题。乍一看,这似乎很容易实现。但事实并非如此。感谢您提供良好的解决方案和解释。
  • 你有一个 ORDER BY (并且没有分组),所以我认为这 保证!?!
  • @Strawberry:MySQL保证的是涉及 MySQL 用户定义变量的表达式的求值顺序。该文档包含对此的警告,并表示评估顺序是“未定义的”。我的答案中的 SELECT 语句 取决于 这些表达式的评估顺序。我们观察到该方法确实有效,并且行为是可重复的;它只是不是 MySQL 的“保证”。
  • 谢谢 - 我不知道 - 虽然我依稀记得有一篇文章指出了变量的特性
【解决方案2】:

试试这个:

SELECT ID, Place, Person
FROM users
GROUP BY Person
ORDER BY FIELD(Place,'London') DESC, Person ASC;

【讨论】:

  • @spencer7593:你看过“GROUP BY Person”吗?你有一个例子说明这不起作用吗?
  • 此查询返回的结果与规范不符。它可以为给定的人返回除“伦敦”行之外的行。 (由于ORDER BY 是在GROUP BY 子句之后 处理的,因此不能保证“London”行将是返回的行。此查询行为的SQL Fiddle 演示(返回其他行比“伦敦”行)在这里:http://sqlfiddle.com/#!2/65fc1/1
【解决方案3】:

您想使用group by 而不是distinct

SELECT ID, Person
FROM users   
GROUP BY ID, Person     
ORDER BY MAX(FIELD(Place, 'London')), Person ASC;

GROUP BYSELECT DISTINCT 做同样的事情。但是,您可以在子句中提及其他字段,例如 HAVINGORDER BY

【讨论】:

  • 我可以看到此查询适用于示例数据,但如果我们要添加一行 5 - Vienna - Anna,我认为这不会返回所需的结果。 (使用MAX() 聚合作为识别London 的方式似乎不是正确的方法。)
  • @spencer7593 。 . . “呃”。我明白你现在在说什么并修复答案。我错过了样本数据有重复的 Annas。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-28
  • 1970-01-01
  • 1970-01-01
  • 2011-10-09
  • 1970-01-01
相关资源
最近更新 更多