具有子链接和父链接的递归CTE

时间:2014-05-29 09:16:58

标签: sql sql-server recursion

您好我有一个存储数据映射的表,例如

Data                  Map
id | letter | type    l | r
------------------    -----
 1 |   AA   | HEAD    5 | 1
 2 |   BB   | HEAD    2 | 1
 3 |   CC   | HEAD    6 | 2
 4 |   DD   | HEAD    3 | 2
 5 |  END-1 |  END    7 | 3
 6 |  END-2 |  END    8 | 4
 7 |  END-3 |  END
 8 |  END-4 |  END

请参阅http://sqlfiddle.com/#!3/4eccfe/5

我想找出来自给定来源的所有END类型链接,例如对于AA,我得到END-1,END-2,END-3;对于BB,我得到END-1,END-2,END-3;对于CC,我得到END-1,END-2,END-3;对于DD,我得到END-4

我用递归CTE写了我想要的东西:

;WITH data(id, letter, type) AS (
    SELECT '1', 'AA', 'HEAD' UNION SELECT '2', 'BB', 'HEAD' UNION SELECT '3', 'CC', 'HEAD' UNION
    SELECT '4', 'DD', 'HEAD' UNION SELECT '5', 'END-1', 'END' UNION SELECT '6', 'END-2', 'END' UNION
    SELECT '7', 'END-3', 'END' UNION SELECT '8', 'END-4', 'END'
), map (l, r) AS (
    SELECT '5', '1' UNION SELECT '2', '1' UNION
    SELECT '6', '2' UNION SELECT '3', '2' UNION
    SELECT '7', '3' UNION SELECT '8', '4'
), my_list (origin, source, target, target_type, sid, tid, level) AS (
    SELECT s.letter, s.letter, t.letter, t.type, s.id, t.id, 0
    FROM data s JOIN map ON (s.id = l OR s.id = r)
    JOIN data t ON (t.id = l OR t.id = r)
    WHERE t.id <> s.id AND s.type <> 'END'
    UNION ALL
    SELECT my_list.origin, s.letter, t.letter, t.type, s.id, t.id, level + 1
    FROM data s JOIN map ON (s.id = l OR s.id = r)
    JOIN data t ON (t.id = l OR t.id = r) JOIN my_list ON s.id = my_list.tid
    WHERE t.id <> s.id AND s.type <> 'END' AND t.id <> my_list.sid
)
SELECT * FROM my_list
WHERE origin = 'BB' AND target_type = 'END'
ORDER BY level
GO

但表现并不是很好(在我真实的桌子上)。然后我意识到连接条件中的OR是导致问题的,然后我尝试使用UNION

my_list (origin, source, target, target_type, sid, tid, level) AS (
    SELECT s.letter, s.letter, t.letter, t.type, s.id, t.id, 0
    FROM data s JOIN map ON s.id = l JOIN data t ON t.id = r
    WHERE s.type <> 'END'
    UNION ALL
    SELECT s.letter, s.letter, t.letter, t.type, s.id, t.id, 0
    FROM data s JOIN map ON s.id = r JOIN data t ON t.id = l
    WHERE s.type <> 'END'
    UNION ALL
    SELECT my_list.origin, s.letter, t.letter, t.type, s.id, t.id, level + 1
    FROM data s JOIN map ON (s.id = l OR s.id = r)
    JOIN data t ON (t.id = l OR t.id = r) JOIN my_list ON s.id = my_list.tid
    WHERE t.id <> s.id AND s.type <> 'END' AND t.id <> my_list.sid
)

差别很大(在我的真实桌子上,时间缩短了一半)。对于上面的例子,我得到了

Table 'Worktable'. Scan count 6, logical reads 100

VS

Table 'Worktable'. Scan count 5, logical reads 75

但是当我尝试为递归部分做同样的事情时,例如

my_list (origin, source, target, target_type, sid, tid, level) AS (
    SELECT s.letter, s.letter, t.letter, t.type, s.id, t.id, 0
    FROM data s JOIN map ON s.id = l JOIN data t ON t.id = r
    WHERE s.type <> 'END'
    UNION ALL
    SELECT s.letter, s.letter, t.letter, t.type, s.id, t.id, 0
    FROM data s JOIN map ON s.id = r JOIN data t ON t.id = l
    WHERE s.type <> 'END'
    UNION ALL
    SELECT my_list.origin, s.letter, t.letter, t.type, s.id, t.id, level + 1
    FROM data s JOIN map ON s.id = l
    JOIN data t ON t.id = r JOIN my_list ON s.id = my_list.tid
    WHERE s.type <> 'END' AND t.id <> my_list.sid
    UNION ALL
    SELECT my_list.origin, s.letter, t.letter, t.type, s.id, t.id, level + 1
    FROM data s JOIN map ON s.id = r
    JOIN data t ON t.id = l JOIN my_list ON s.id = my_list.tid
    WHERE s.type <> 'END' AND t.id <> my_list.sid
)

结果变慢(在我的真实桌子上,慢5倍)。

我想知道为什么它会变慢并且是否有其他方法可以摆脱OR以加快查询速度?数据库是MS SQL SERVER 2008R2

谢谢

1 个答案:

答案 0 :(得分:1)

我可能错了,但你不能推断谓词:

WHERE origin = 'BB'
在CTE里面。即:

;WITH my_list (origin, source, target, target_type, sid, tid, level) AS (
SELECT s.letter, s.letter, t.letter, t.type, s.id, t.id, 0
FROM data s 
    JOIN map 
        ON s.id = l 
    JOIN data t 
        ON t.id = r
WHERE s.letter = 'BB'
UNION ALL
SELECT s.letter, s.letter, t.letter, t.type, s.id, t.id, 0
FROM data s 
    JOIN map 
        ON s.id = r 
    JOIN data t 
        ON t.id = l
WHERE s.letter = 'BB'
UNION ALL
SELECT my_list.origin, s.letter, t.letter, t.type, s.id, t.id, level + 1 
    FROM data s 
    JOIN map 
        ON (s.id = l OR s.id = r)
JOIN data t 
        ON (t.id = l OR t.id = r) 
    JOIN my_list 
        ON s.id = my_list.tid
WHERE t.id <> s.id 
      AND s.type <> 'END' 
      AND t.id <> my_list.sid
)
SELECT * FROM my_list
WHERE origin = 'BB' AND target_type = 'END'
ORDER BY level

这会提高性能吗?

相关问题