Ars*_*ius 3 mysql subquery limits mysql-5.6 greatest-n-per-group
由于 MySql 不支持子查询中的主别名引用,因此创建具有多行的查询有点困难,您必须限制左连接表的行。
让我们概述一下简单的例子。
假设有两个表:
CREATE TABLE `items` (
`id` INT NOT NULL AUTO_INCREMENT,
`parent` VARCHAR(45) CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci' NOT NULL,
PRIMARY KEY (`id`));
CREATE TABLE `sub_items` (
`id` INT NOT NULL AUTO_INCREMENT,
`child` VARCHAR(45) CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci' NOT NULL,
`parent_id` INT NOT NULL,
PRIMARY KEY (`id`));
Run Code Online (Sandbox Code Playgroud)
内容如下:
INSERT INTO `items` (`parent`) VALUES ('Main item1');
INSERT INTO `items` (`parent`) VALUES ('Main item2');
INSERT INTO `items` (`parent`) VALUES ('Main item3');
INSERT INTO `sub_items` (`child`, `parent_id`) VALUES ('Child Item11', '1');
INSERT INTO `sub_items` (`child`, `parent_id`) VALUES ('Child Item12', '1');
INSERT INTO `sub_items` (`child`, `parent_id`) VALUES ('Child Item13', '1');
INSERT INTO `sub_items` (`child`, `parent_id`) VALUES ('Child Item21', '2');
INSERT INTO `sub_items` (`child`, `parent_id`) VALUES ('Child Item22', '2');
INSERT INTO `sub_items` (`child`, `parent_id`) VALUES ('Child Item23', '2');
INSERT INTO `sub_items` (`child`, `parent_id`) VALUES ('Child Item24', '2');
INSERT INTO `sub_items` (`child`, `parent_id`) VALUES ('Child Item31', '3');
Run Code Online (Sandbox Code Playgroud)
现在让我们创建如下查询:
SELECT i.*, si.child, si.parent_id FROM items as i
LEFT JOIN sub_items as si
ON i.id = si.parent_id;
Run Code Online (Sandbox Code Playgroud)
结果将是:
'1', 'Main item1', 'Child Item11', '1'
'1', 'Main item1', 'Child Item12', '1'
'1', 'Main item1', 'Child Item13', '1'
'2', 'Main item2', 'Child Item21', '2'
'2', 'Main item2', 'Child Item22', '2'
'2', 'Main item2', 'Child Item23', '2'
'2', 'Main item2', 'Child Item24', '2'
'3', 'Main item3', 'Child Item31', '3'
Run Code Online (Sandbox Code Playgroud)
现在的挑战是限制连接表行(在本例中限制为 2 行):
'1', 'Main item1', 'Child Item11', '1'
'1', 'Main item1', 'Child Item12', '1'
'2', 'Main item2', 'Child Item21', '2'
'2', 'Main item2', 'Child Item22', '2'
'3', 'Main item3', 'Child Item31', '3'
Run Code Online (Sandbox Code Playgroud)
如果我们尝试使用下面的查询来达到预期的结果,MySQL 将抛出错误:
SELECT i.*, si.child, si.parent_id FROM items as i
LEFT JOIN (SELECT * FROM sub_items WHERE parent_id = i.id LIMIT 2) as si
ON i.id = si.parent_id;
Run Code Online (Sandbox Code Playgroud)
那么,对于每个左连接子查询,使用 2 个(或更多)限制行来获取上述结果的替代方法是什么?
您使用什么版本的 MySQL?MySQL 8 支持 LATERAL,您需要能够i从派生表中引用它:
SELECT i.*, si.child, si.parent_id
FROM items as i
LEFT JOIN LATERAL (SELECT *
FROM sub_items
WHERE parent_id = i.id
LIMIT 2) as si
ON i.id = si.parent_id;
Run Code Online (Sandbox Code Playgroud)
考虑将 ORDER BY 添加到您的子选择中。
执行类似操作的其他方法是使用窗口函数,例如:
row_number() over (partition by ... order by ...) as rn
Run Code Online (Sandbox Code Playgroud)
并在外层过滤rn。
然而,窗口函数仅在 MySQL 8 中受支持,但您可以使用变量来模拟它们:
SELECT i.*, si.child, si.parent_id, si.rn
FROM items as i
LEFT JOIN (
SELECT c.*,
@row_number:=CASE WHEN @parent_id = parent_id
THEN @row_number + 1
ELSE 1
END AS rn,
@parent_id := parent_id
FROM sub_items as c
CROSS JOIN (select @row_number := 1) as x
CROSS JOIN (select @parent_id := -1) as y
ORDER BY parent_id
) as si
ON i.id = si.parent_id
AND si.rn <= 2
;
Run Code Online (Sandbox Code Playgroud)
这个想法是通过parent_id对子查询进行排序,为每行将row_num增加1,并且一旦parent_id发生变化,将row_num重置为1。
您可以阅读有关该想法的更多信息,例如:mysql-row_number
| 归档时间: |
|
| 查看次数: |
26070 次 |
| 最近记录: |