MYSQL中的递归查询?

时间:2011-09-30 23:29:44

标签: mysql sql recursive-query

我认为我的问题是通过'递归查询'解决的,但由于MySQL不支持递归查询,我试图使用相邻的列表模型。这应该不是问题,因为我知道我想要走多远。

以下是我需要做的一个例子: 表类:

dept    classNum    prereqDept  prereqClassNum
BIO     465         BIO         335
EE      405         EE          325
EE      325         EE          120
BIO     465         EE          120
BIO     335         BIO         225
BIO     225         CHEM        110
BIO     225         BIO         105

我需要的是某个级别的所有类别(比方说400),其所有先决条件最多可达3级。 所以我会得到像

这样的东西
dept    classNum    prereqDept  prereqClassNum
BIO     465         BIO         335
BIO     465         BIO         225
BIO     465         CHEM        110
BIO     465         BIO         105
EE      405         EE          325
EE      405         EE          120
....

我知道如果我想要深入3级,我需要使用3-LEFT JOIN,但我无法弄清楚如何设置这些连接以获得我需要的东西。 有任何想法吗? 我会得到你的帮助!

P.S。我根本无法改变表结构。

3 个答案:

答案 0 :(得分:2)

我已经运行了这个并且它获得了所有类(在此示例中)最多3个先决条件,但是可以通过基于“IDSeq =?”复制对来扩展到4,5,6。 / p>

这里的关键要素是根据公共Dept + ClassNum为每条记录分配一个数字,每次在每次组更改时从1开始。为此,我已将SQL变量应用于FIRST确保每个组都按顺序排列1,2,3 ... 1,2,... 1,... 1,2,3,4,5 ..等等......

内部查询的结果

Result of inner query

一旦完成,我们可以做一个简单的组,没有其他复杂的连接,连接,联合等...只需应用基于已知序列的IF()的max()。正如你可以看到的模式,我得到的是它的先决条件Dept和ClassNum所提供的任何内容,即“1”st记录,然后是“2”和“3”rd,但可以应用对于“4”,“5”等等。

通过使用max(if()),每个类总是有1个序列,但有时只有2个,更不用说3,4或5.所以,如果没有值,那么最少填充空格,因此不会显示null。然后,如果/当有一个值时,MAX()将在它到达时取代空格......

最后的查询很惊人,可能只是你需要的。

select
      NewSet.Dept,
      NewSet.ClassNum,
      max( if( NewSet.IDSeq = 1, NewSet.PreReqDept, '    ' )) FirstDept,
      max( if( NewSet.IDSeq = 1, NewSet.PreReqClassNum, '    ' )) FirstClassNum,
      max( if( NewSet.IDSeq = 2, NewSet.PreReqDept, '    ' )) SecondDept,
      max( if( NewSet.IDSeq = 2, NewSet.PreReqClassNum, '    ' )) SecondClassNum,
      max( if( NewSet.IDSeq = 3, NewSet.PreReqDept, '    ' )) ThirdDept,
      max( if( NewSet.IDSeq = 3, NewSet.PreReqClassNum, '    ' )) ThirdClassNum
   from 
      ( select 
             @orig := @orig +1 as OrigSeq,
             @seq := if( concat(P.Dept, P.ClassNum ) = @LastGrp, @seq +1, 1 ) as IDSeq,
             @LastGrp := concat( P.Dept,  P.ClassNum ) NextGrp,
             P.Dept,
             P.ClassNum,
             P.PreReqDept,
             P.PreReqClassNum
          from 
             PreReqs P,
             ( select @orig := 0, @seq := 0, @LastGrp := '' ) x
          order by 
             Dept,
             ClassNum ) NewSet
   group by
      NewSet.Dept,
      NewSet.ClassNum
   order by 
      NewSet.Dept,
      NewSet.ClassNum

答案 1 :(得分:1)

我会这样做(不确定是否有更容易)。首先在一个级别获得依赖(简单):

SELECT * FROM table;

然后两级下来:

SELECT t.dept AS dept, t.classNum AS classNum, t2.prereqDept AS prereqDept, t2.prereqClassNum AS prereqClassNum FROM table AS t
 LEFT JOIN table AS t2 WHERE t2.classNum = t.prereqClassNum;

重复使用三级:

SELECT t3.dept AS dept, t3.classNum AS classNum, t4.prereqDept AS prereqDept, t4.prereqClassNum AS prereqClassNum 
FROM (
    SELECT t.dept AS dept, t.classNum AS classNum, t2.prereqDept AS prereqDept, t2.prereqClassNum AS prereqClassNum FROM table AS t
    LEFT JOIN table AS t2 WHERE t2.classNum = t.prereqClassNum
) AS t3
LEFT JOIN table AS t4 WHERE t4.classNum = t3.prereqClassNum;

最后,你可以只对这三个查询进行联合。

(SELECT * FROM table)
UNION
(SELECT t.dept AS dept, t.classNum AS classNum, t2.prereqDept AS prereqDept, t2.prereqClassNum AS prereqClassNum FROM table AS t
LEFT JOIN table AS t2 WHERE t2.classNum = t.prereqClassNum)
UNION
(SELECT t3.dept AS dept, t3.classNum AS classNum, t4.prereqDept AS prereqDept, t4.prereqClassNum AS prereqClassNum 
FROM (
    SELECT t.dept AS dept, t.classNum AS classNum, t2.prereqDept AS prereqDept, t2.prereqClassNum AS prereqClassNum FROM table AS t
    LEFT JOIN table AS t2 WHERE t2.classNum = t.prereqClassNum
) AS t3
LEFT JOIN table AS t4 WHERE t4.classNum = t3.prereqClassNum);

我没有检查它是否有效...但是这样的事情应该有用......

答案 2 :(得分:1)

嗯。试试这个:

SELECT * FROM
(
    (
        SELECT t1.dept, t1.classNum, t1.prereqDept, t1.prereqClassNum
        FROM class AS t1
        WHERE t1.classNum >= 400
    )
    UNION
    (
        SELECT t1.dept, t1.classNum, t2.prereqDept, t2.prereqClassNum
        FROM class AS t1
        JOIN class AS t2 ON (t1.prereqDept = t2.dept AND t1.prereqClassNum = t2.classNum)
        WHERE t1.classNum >= 400
    )
    UNION
    (
        SELECT t1.dept, t1.classNum, t3.prereqDept, t3.prereqClassNum
        FROM class AS t1
        JOIN class AS t2 ON (t1.prereqDept = t2.dept AND t1.prereqClassNum = t2.classNum)
        JOIN class AS t3 ON (t2.prereqDept = t3.dept AND t2.prereqClassNum = t3.classNum)
        WHERE t1.classNum >= 400
    )
) AS t4
ORDER BY dept, classNum, prereqDept, prereqClassNum