我了解ORDER BY子句的工作方式以及FIELD()函数的工作方式。
我想了解的是两者如何协同工作。
如何检索行以及如何导出排序顺序

+----+---------+
| id |  name   |
+----+---------+
|  1 | stan    |
|  2 | kyle    |
|  3 | kenny   |
|  4 | cartman |
+----+---------+ 

SELECT * FROM mytable WHERE id IN (3,2,1,4) ORDER BY FIELD(id,3,2,1,4)


上述查询将导致

+----+---------+
| id |  name   |
+----+---------+
|  3 | kenny   |
|  2 | kyle    |
|  1 | stan    |
|  4 | cartman |
+----+---------+ 


类似于说ORDER BY 3、2、1、4

问题


在内部如何工作?
MySQL如何获取行并计算排序顺序?
MySQL如何知道它必须按id列排序?


评论

尝试使用以下查询变体:SELECT *,FIELD(id,3,2,1,4)AS f FROM mytable WHERE id IN(3,2,1,4);然后添加ORDER BY f或ORDER BY FIELD(id,3,2,1,4),然后重试。

#1 楼

记录

SELECT * FROM mytable WHERE id IN (1,2,3,4) ORDER BY FIELD(id,3,2,1,4);


应该也可以工作,因为您不必在WHERE子句中订购列表

关于其工作方式,



FIELD()是一个函数,如果要搜索的值存在,该函数将返回逗号分隔列表的索引位置。


如果id id = 1,则FIELD(id,3,2,1,4)返回3(列表中的位置1)
如果id = 2,则FIELD(id,3, 2,1,4)返回2(列表中2的位置)
如果id = 3,则FIELD(id,3,2,1,4)返回1(列表中3的位置)
如果id id = 4,则FIELD(id,3,2,1,4)返回4(列表中的位置,其中4在列表中)。
如果id =其他,则FIELD(id,3, 2,1,4)返回0(不在列表中)


ORDER BY值由FIELD()返回的值评估

您可以创建各种订单数量

例如,使用IF()函数

SELECT * FROM mytable
WHERE id IN (1,2,3,4)
ORDER BY IF(FIELD(id,3,2,1,4)=0,1,0),FIELD(id,3,2,1,4);


这将导致前4个ID出现在t列表中的操作,否则,它会显示在底部。为什么?

ORDER BY中,您得到0或1。


如果第一列为0,则出现前4个ID中的任何一个
如果第一列为1,则让它出现在后面

让我们在第一列中用DESC翻转它

SELECT * FROM mytable
WHERE id IN (1,2,3,4)
ORDER BY IF(FIELD(id,3,2,1,4)=0,1,0) DESC,FIELD(id,3,2,1,4);

,您仍然会得到0或1。

如果第一列为1,则除了前4个ID之外,都显示其他内容。
如果第一列为0,则使前4个ID按原始顺序显示

您的实际问题

如果您真的想要此内部说明,请转到本书的第189页和第192页



进行真正的深潜。

本质上,存在一个名为ORDER BYORDER *order表达式树)的C ++类。在ORDER BY中,JOIN::prepare用于名为*order的函数中。为什么在setup_order()班级中间?每个查询,甚至是针对单个表的查询都始终作为JOIN处理(请参阅我的文章JOIN条件和WHERE条件之间是否存在执行区别?) JOIN

显然,sql/sql_select.cc树将保留ORDER BY的评估。因此,数字0、1、2、3、4是在对涉及的行进行引用的同时正在排序的值。

评论


这是一个非常出色的解释。使用这些方法,我可以得到3个订单,第一个优先值是集合的最大值,然后是FIELD,然后是不是FIELD集合中的那些的另一列。我前段时间梦dream以求的东西。感谢您抽出宝贵的时间来真正解释它是如何工作的。

–蜥蜴
16年5月4日在23:03

假设IN和FIELD中都包含N个值。在此示例中,N = 4。我是否正确理解该查询将至少执行〜N ^ 2个操作。因为每个FIELD计算都会为每行进行一次〜N个比较。如果是这样,那么对于大N来说这相当慢,也许这不是一个很好的方法?

–盖尔曼
19 Mar 15 '19在15:53

@Gherman FIELD()函数应该是O(1)操作,因为FIELD()具有数字索引ID。因此,除了基于行的O(n)外,我什么都看不到。我看不到FIELD()要做任何迭代操作,例如GREATEST()。

– RolandoMySQLDBA
19 Mar 15 '19在18:03



@RolandoMySQLDBA我的观点是,如果FIELD具有N个要比较的参数,则它将执行N个比较。如果不通过O(N)比较一个数字和N个其他数字,还有什么其他方法?我能想到的唯一可能性是通过特殊数据结构(例如哈希或参数树)进行某种优化。实际上,我知道IN确实具有这种优化。我不了解FIELD。 “数字索引”是什么意思?

–盖尔曼
19 Mar 15 '19在20:18

嘿@RaymondNijland,CASE语句更容易理解,在这种情况下,语法糖的文字更少。

– RolandoMySQLDBA
19-10-3在14:08

#2 楼

也许这与实际代码相差太远,因此与您想要的代码相比还不够低:

当MySQL无法使用索引来按排序顺序检索数据时,它将创建一个具有所有选定列的临时表/结果集还有一些其他数据(其中之一是某种类型的列,用于存储每一行​​的ORDER BY表达式值的结果),然后将此tmp表发送到“ filesort”程序,其中包含要对哪一列进行排序的信息。之后,这些行将按排序顺序排列,以便可以一一挑选它们并返回选定的列。

评论


该解释未考虑如何计算FIELD的功能。恐怕会对性能产生重大影响。

–盖尔曼
19年3月15日在16:07

@Gherman我不这么认为,除非您使用了很长的参数列表(因为该函数与参数数量成线性关系。数据访问比简单比较慢一个数量级)。

– jkavalik
19 Mar 15 '19在17:23

是的,一长串参数。此示例中的参数与记录一样多。

–盖尔曼
19年3月15日在20:20

我只会标注成百上千的标签,然后无论如何都会遇到其他问题(查询大小等)。

– jkavalik
19 Mar 15 '19在20:42

为什么没有数百个结果?有很多吗

–盖尔曼
19 Mar 15 '19在22:18