我有以下查询:
SELECT child.id_catalog_category AS id_category, ancestor.id_catalog_category AS tree
FROM catalog_category AS child
JOIN catalog_category AS ancestor
ON (child.lft BETWEEN ancestor.lft AND ancestor.rgt)
WHERE ancestor.id_catalog_category != 1
ORDER BY id_category ASC, tree ASC
是重建分层产品类别的二叉树。对于一个id_category,我们最多可以有4个“树”值,如示例所示:
id_category / tree
3 2
3 3
4 2
4 3
4 4
5 2
5 3
5 5
6 2
6 3
6 6
7 2
7 3
7 7
期望的结果应该是:
id / id_category / tree
1 3 2
2 3 3
3 null null
4 null null
1 4 2
2 4 3
3 4 4
4 null null .....
在单词中,我想为每个id_category添加1到4的范围id,如果id_category的值小于4,则应显示空值。
此致
答案 0 :(得分:1)
要复制您指定的数据集,我这样做是为了得到一个工作集:
CREATE TABLE cc (id_category INT, tree INT);
INSERT INTO cc VALUES (3,2),(3,3),(4,2),(4,3),(4,4),(5,2),(5,3),
(5,5),(6,2),(6,3),(6,6),(7,2),(7,3),(7,7);
SELECT cc.* FROM cc ORDER BY 1,2;
SQL小提琴:http://sqlfiddle.com/#!2/16249/3
以下是我如何处理这个问题。首先,我会得到一个明确的id_category
值列表。这很简单。 (我可以使用DISTINCT关键字而不是GROUP BY,无论哪个。)
SELECT id_category
FROM cc
GROUP BY id_category
ORDER BY id_category
然后,我将从每个行生成四行。所以,我打算将之前的查询包装为内联视图(将其括在一组括号中,给它一个别名,并引用整个混乱,就像它只是一个表名。这样的东西:
SELECT c.id_category, j_.j
FROM (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) j_
JOIN (
SELECT cc.id_category
FROM cc cc
GROUP BY cc.id_category
ORDER BY cc.id_category
) c
ORDER BY c.id_category, j_.j
我正在使用另一个内联视图返回1到4的整数,并执行CROSS JOIN以获得每个不同id_category的四行。这基本上让我得到了我想要返回的结果集的大纲...但是我没有树列的任何值(除NULL之外)。
所以现在,我需要备份,并开始处理另一个行集,基本上是cc表中的有序集,但这一次,包括树列的值。我并不关心获取正好四行,只是在树列中具有值的行。再次,非常直截了当:
SELECT s.id_category_id, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
但是现在,我想在每个id_category中为每个行分配一个相对的行号。我可以通过将该查询包装在一组括号中,给它一个别名,并将其视为一个表,如下所示:
SELECT @i := IF(r.id_category = @prev_idcat,@i + 1,1) AS i
, @prev_idcat := r.id_category AS id_category
, r.tree
FROM (SELECT @i := 0, @prev_idcat := NULL) i_
JOIN (
SELECT s.id_category, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
) r
我正在使用带有用户变量的MySQL技巧,为每个不同的id_category分配从1开始的升序整数值。这里的技巧是让MySQL为我排序行(在内联视图中别名为r,并在用户变量中“保存”前一行的id_category,因此我可以将它与下一行进行比较。
现在我们真的已经到了在MySQL中使用Common Table Expressions真的非常非常好的地步。但由于它们不是,我们向前推进,嵌套我们的内联视图。
所以我要给每个“行编号”查询一个别名,并引用它们就像它们是表格一样;查询将采用......
的形式SELECT b.*, q.*
FROM ( ) b
LEFT
JOIN ( ) q
ON q.id_category = b.id_category AND q.i = b.j
(我们省略了这些内联视图的内容,只是为了概述该语句的真正用途。)
这将开始看起来丑陋,但这是魔术发生的地方。 我从b中为每个id_category提取四行,然后将其加入q,匹配id_category和“行号”。它是LEFT OUTER连接,所以我将从b获取所有行,并从q中选取任何“匹配”行。
SELECT b.id_category, q.tree
FROM (SELECT c.id_category, j_.j
FROM (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
) j_
JOIN (
SELECT cc.id_category
FROM cc cc
GROUP BY cc.id_category
ORDER BY cc.id_category
) c
ORDER BY c.id_category, j_.j
) b
LEFT
JOIN (SELECT @i := IF(r.id_category = @prev_idcat,@i + 1,1) AS i
, @prev_idcat := r.id_category AS id_category
, r.tree
FROM (SELECT @i := 0, @prev_idcat := NULL) i_
JOIN (
SELECT s.id_category, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
) r
) q
ON q.id_category = b.id_category AND q.i = b.j
ORDER BY b.id_category, b.j
规范中唯一剩下的就是为id
列生成一个值。如果我插入表格,我可以使用AUTO_INCREMENT列为我做。但是如果没有这个,我生成id
值的最方便的地方是内联视图别名为b
。稍微调整一下,最后,我们有一个查询的怪异,它返回指定的结果集:
SELECT b.k AS id, b.id_category, q.tree
FROM (SELECT @k := @k + 1 AS k
, c.id_category
, j_.j
FROM (SELECT @k := 0) k_
JOIN (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
) j_
JOIN (
SELECT cc.id_category
FROM cc cc
GROUP BY cc.id_category
ORDER BY cc.id_category
) c
ORDER BY c.id_category, j_.j
) b
LEFT
JOIN (SELECT @i := IF(r.id_category = @prev_idcat,@i + 1,1) AS i
, @prev_idcat := r.id_category AS id_category
, r.tree
FROM (SELECT @i := 0, @prev_idcat := NULL) i_
JOIN (
SELECT s.id_category, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
) r
) q
ON q.id_category = b.id_category AND q.i = b.j
ORDER BY b.id_category, b.j
要使其与行集一起使用,您可以使用括在括号中的查询替换对cc
表的每个引用。或者,您可以像我一样创建一个名为cc的表,并将查询结果插入其中。
某人可能有一个更简单的SQL语句,可以可靠地生成相同的结果集。我很想学习一种更简单的方法。