MySQL结果没有正确排序?在子查询中排序

时间:2015-12-07 07:53:21

标签: mysql sorting

我发现有时,我的结果使用以下SQL(MySQL)查询错误地排序:

SELECT u.id, u.firstName, u.lastName, u.email, u.ssoProfile, u.vip, SUM(p.number) AS totalPoints 
FROM (
      SELECT u.id FROM user u 
      ORDER BY u.firstName ASC, u.lastName ASC, u.email ASC 
      LIMIT 100, 10
) u2 
INNER JOIN `user` u 
ON u2.id = u.id 
LEFT JOIN `point` p 
ON u.id = p.user 
AND p.expiryDate > NOW() 
GROUP BY u.id

正如您所看到的,我在子查询中按用户排序。但是,我注意到有时候很少会得到不符合指定顺序的结果。我想我需要在外部查询上订购?但我想了解这个查询的错误。为什么我的用户订单搞砸了?

更新

我在外部查询中添加了一个订单。令人惊讶的是,我发现我的结果仍然可以按错误的顺序返回

SELECT u.id, u.firstName, u.lastName, u.email, u.ssoProfile, u.vip, SUM(p.number) AS totalPoints 
FROM (
      SELECT u.id FROM user u 
      ORDER BY u.firstName ASC, u.lastName ASC, u.email ASC 
      LIMIT 100, 10
) u2 
INNER JOIN `user` u 
ON u2.id = u.id 
LEFT JOIN `point` p 
ON u.id = p.user 
AND p.expiryDate > NOW() 
GROUP BY u.id
ORDER BY u.firstName ASC, u.lastName ASC, u.email ASC

结果如下:

[ { id: 4834, firstName: 'F01', lastName: 'L01' },
  { id: 4835, firstName: 'F00', lastName: 'L00' }, // << notice F00 is after F01?
  { id: 4836, firstName: 'F02', lastName: 'L02' },
  { id: 4837, firstName: 'F03', lastName: 'L03' },
  { id: 4838, firstName: 'F04', lastName: 'L04' },
  { id: 4839, firstName: 'F05', lastName: 'L05' },
  { id: 4840, firstName: 'F06', lastName: 'L06' },
  { id: 4841, firstName: 'F07', lastName: 'L07' },
  { id: 4842, firstName: 'F08', lastName: 'L08' },
  { id: 4843, firstName: 'F09', lastName: 'L09' } ]

@ lad2025提到它可能是因为我在评论中错误地使用了GROUP BY。但是,我检查了ID匹配。例如,F00确实属于用户ID 4835。

2 个答案:

答案 0 :(得分:1)

首先,@ lad2025和@Tim Biegeleisen都给出了正确的评论。 查询优化器更改查询,以使结果集保持不变,并且时间保持尽可能低。 查询优化器会更改命令的顺序,例如JOINsWHERE条件。

这些链接可以为您提供有关查询优化如何工作的有用信息(link1link2)。

在MySQL中,您可以使用EXPLAIN命令查看在查询优化更改后如何执行实际查询。

关于解决您的问题,您只需在外部查询中复制ORDER BY,因为这样您就可以告诉RDBMS您也希望对结果进行排序。

我怀疑是在查询重新排序(看看这个 - &gt; heuristic optimisation)改变了一些东西,因为你在子查询中有与外部查询相同的表(不是最准确的解释,我知道)。 这很难确定,因为它取决于您所拥有的索引,执行时的内存大小,表的大小等等。

您的查询应如下所示:

SELECT u.id, u.firstName, u.lastName, u.email, u.ssoProfile, u.vip, SUM(p.number) AS totalPoints 
FROM (
      SELECT u.id FROM user u 
      ORDER BY u.firstName ASC, u.lastName ASC, u.email ASC 
      LIMIT 100, 10
) u2 
INNER JOIN `user` u 
ON u2.id = u.id 
LEFT JOIN `point` p 
ON u.id = p.user 
AND p.expiryDate > NOW() 
GROUP BY u.id
ORDER BY u.firstName ASC, u.lastName ASC, u.email ASC 

我要添加的一个注意事项是,您应该仍然在子查询中保留ORDER BY,因为LIMIT条件会在ORDER BY之后执行,因此您最终会遇到不同的情况结果与原始查询进行比较。

答案 1 :(得分:0)

您已经获得了正确答案。您需要ORDER BY来保证订购结果。子查询中的订单不一定会影响结果的顺序。所以解决方案是添加

ORDER BY u.firstName, u.lastName, u.email

在您的查询结束时。

由于您仍然以错误的顺序获取数据,因此必须是其他错误。

最有可能的是查询为您提供了正确排序的数据,但此订单稍后会更改。所以也许你可以在一个应用程序中显示这个,可能是在重新排序记录的列表框等中。直接使用您的DBMS检查您的查询,看看您是否正确订购了记录。

另一种可能性是您没有注意到空白或不可打印的字符,即可能是'F01'而不是'F01',这就是它在'F00'之前出现的原因。