【发布时间】:2014-07-18 18:00:18
【问题描述】:
您好,我有一个存储数据映射的表,例如
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
对
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
谢谢
【问题讨论】:
-
你真的需要 or/union 结构吗?你不能从一端开始并朝一个方向移动可以这么说=即
select distinct m1.r from map m1 left join map m2 on m1.r = m2.l where m2.l is null -
单向能得到我想要的我不介意
-
我不认为你可以完全摆脱
ORs,但这可能会被简化。一个重要问题 - 您的示例数据集在map中不同,其 id 代表'END'行 - 您的实际数据是否如此? -
另外,他们都在左边……这也成立吗?我很好奇,因为这在某种程度上暗示你们的关系不是双向的。
-
是的,END节点都在左边,只有HEAD节点可以同时在左右(例如'BB')
标签: sql sql-server recursion