假设我有四个表:tbl1
... tbl4
。每个字段都有一个唯一的数字id
字段。 tbl1
,tbl2
和tbl3
每个都具有序列中下一个表的外键字段。例如。 tbl1
具有tbl2_id
外键字段,依此类推。每个表还具有一个字段order
(以及与该问题无关的其他字段)。
直接连接所有四个表以返回tbl1的所有行以及其他三个字段中的相应字段。通过ORDER BY
字段的特定order
组合对结果集进行排序也很容易。仅返回与id
中某些特定tbl1
相对应的行也很容易,例如WHERE tbl1.id = 7777
。
问题:哪种查询最有效地返回(例如)从id=7777
对应的行开始的100行,以order
字段的特定组合确定的顺序?
使用ROW_NUMBER
或(在MySQL版本<8中对其进行仿真)来获取id = 7777行的位置,然后在同一查询的新版本中使用该位置来设置LIMIT
中的偏移量条款将是一种方法。 (之间有读取锁定。)但是可以在单个查询中完成吗?
# FIRST QUERY: get row number of result row where tbl1.id = 7777
SELECT x.row_number
FROM
(SELECT @row_number:=@row_number+1 AS row_number, tbl1.id AS id
FROM (SELECT @row_number:=0) AS t, tbl1
INNER JOIN tbl2 ON tbl2.id = tbl1.tbl2_id
INNER JOIN tbl3 ON tbl3.id = tbl2.tbl3_id
INNER JOIN tbl4 ON tbl4.id = tbl3.tbl4_id
WHERE <some conditions>
ORDER BY tbl4.order, tbl3.order, tbl2.order, tbl1.order
) AS x
WHERE id=7777;
存储以上查询中的行号,并用它来绑定以下查询中的:offset
。
# SECOND QUERY : Get 100 rows starting from the one with id=7777
SELECT x.field1, x.field2, <etc.>
FROM
(SELECT @row_number:=@row_number+1 AS row_number, field1, field2
FROM (SELECT @row_number:=0) AS t, tbl1
INNER JOIN tbl2 ON tbl2.id = tbl1.tbl2_id
INNER JOIN tbl3 ON tbl3.id = tbl2.tbl3_id
INNER JOIN tbl4 ON tbl4.id = tbl3.tbl4_id
WHERE <same conditions as before>
ORDER BY tbl4.order, tbl3.order, tbl2.order, tbl1.order
) AS x
LIMIT :offset, 100;
澄清问题
在一般情况下,您不会要求输入WHERE id1 > 7777
。相反,您有一个(11,22,33,44)
元组,并且您想“从上次中断的地方继续”。
两次讨论,并
这很混乱,但并非不可能。参见Iterating through a compound key。 Ig给出了一个使用2列进行操作的示例;来自4个表的4列是此类的扩展。
一个变体
[实际上实现这种方法时,我发现让“ 100”(LIMIT
)灵活些可以更容易地思考。这个想法是:向前前进100行(使用LIMIT 100,1
)。假设您得到(111,222,333,444)。如果您当前位于(111,...),则处理id2 / 3/4。如果是(113,...),则执行WHERE id1 < 113
并保留id2 / 3/4的任何规范。这意味着要获取少于100行,但它使您不愿开始id1 = 113。
即,它涉及构造具有1到4个条件的WHERE
子句。
在所有情况下,您的查询都显示为ORDER BY id1, id2, id3, id4
。 LIMIT
的唯一用途是在探针中以找出第100行(带有LIMIT 100,1
)多远。
我想我可以为此挖出一些旧的Perl代码。