为了复制您指定的数据集,我这样做是为了获得一个工作集:
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 中提供公共表表达式的地步,这将非常非常好。但由于它们不是,我们向前推进,嵌套我们的内联视图。
因此,我将为每个“行编号”查询赋予一个别名,并像引用它们一样引用它们;查询将采用以下形式...
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 和“行号”。这是一个左外连接,所以我要从 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 语句,可以可靠地产生相同的结果集。我很想学习一种更简单的方法。