JAF*_*JAF 5 performance sql-server-2008 sql-server recursive query-performance
我有一个大致像这样的架构(这是我实际架构的简化):
CREATE TABLE foo (
key1 NUMERIC(6) NOT NULL,
key2 VARCHAR(32) NOT NULL,
val VARCHAR(255) NULL,
CONSTRAINT foo_pk PRIMARY KEY (key1, key2) --PK on key1, key2
)
GO
CREATE TABLE bar (
key1 NUMERIC(6) NOT NULL,
key2 VARCHAR(32) NOT NULL,
val VARCHAR(255) NULL,
CONSTRAINT bar_pk PRIMARY KEY (key1, key2) --PK on key1, key2
)
GO
CREATE TABLE aliases (
id VARCHAR(32) NOT NULL PRIMARY KEY,
text VARCHAR(255) NOT NULL,
CONSTRAINT aliases_uk UNIQUE (text) --PK on id, unique constraint on text
)
GO
INSERT INTO aliases (id, text)
SELECT REPLACE(NEWID(), '-', ''), 'Text1' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text2' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text3' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text4' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text5' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text6' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text7' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text8' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text9' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text10' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text11' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text12' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text13' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text14' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text15' UNION ALL
SELECT REPLACE(NEWID(), '-', ''), 'Text16';
GO
BEGIN
DECLARE @i INT = 1;
WHILE (@i <= 234)
BEGIN
INSERT INTO foo (key1, key2, val) SELECT @i, id, id FROM aliases;
SET @i = @i + 1;
END;
INSERT INTO bar (key1, key2, val) SELECT key1 + 234, key2, val FROM foo;
END;
GO
Run Code Online (Sandbox Code Playgroud)
我想获得所有key1
和key2
对的列表(使用 的用户友好名称key2
,将val
s的用户友好别名连接起来。我最初的尝试如下所示:
WITH
foos_and_bars (key1, key2, val) AS (
SELECT key1, key2, val FROM foo UNION ALL
SELECT key1, key2, val FROM bar),
texts (key1, key2_name, val_text, rnum) AS (
SELECT key1, a1.text, a2.text, ROW_NUMBER() OVER (PARTITION BY key1, key2 ORDER BY val)
FROM foos_and_bars
JOIN aliases a1 ON a1.id = foos_and_bars.key2
LEFT JOIN aliases a2 ON a2.id = foos_and_bars.val),
partitioned (key1, key2_name, val_text, rnum, maxnum) AS (
SELECT key1, key2_name, val_text, rnum, MAX(rnum) OVER (PARTITION BY key1, key2_name)
FROM texts),
recurse (key1, key2_name, val_texts, rnum, maxnum) AS (
SELECT key1, key2_name, val_text, rnum, maxnum
FROM partitioned
WHERE rnum = 1
UNION ALL
SELECT r.key1,
r.key2_name,
CAST(r.val_texts + CHAR(13) + CHAR(10) + p.val_text AS VARCHAR(255)),
p.rnum,
r.maxnum
FROM recurse r
JOIN partitioned p
ON p.key1 = r.key1
AND p.key2_name = r.key2_name
AND p.rnum = r.rnum + 1)
SELECT * FROM recurse WHERE rnum = maxnum;
Run Code Online (Sandbox Code Playgroud)
它有效,我在不到一秒的时间内获得了 7480 行,但随后 SSMS 运行了不少于 2 分 40 秒,让最后 8 行感到困惑。最后几行并不比第一行复杂(事实上,当前数据在系统中,rnum
并且maxnum
永远不会大于 1),并且由于这些表是专门为模拟这个 SE 问题而创建的,因此不应该有锁。
该问题似乎仅限于递归 CTE,因为从上到下进行选择以partitioned
在不到一秒的时间内生成 7488 行。CTE 会卡在什么地方?
执行计划(使用与简化模式匹配的设置):
输出将进入一个视图,该视图将支持 Sybase 窗口,该窗口将用于搜索记录。因此,它需要能够检索所有行,尽管默认情况下程序一次提取 1000 行。
递归 SQL 查询的锚点部分生成所有 7488 行。在我的机器上,查询的那部分在 100 毫秒内完成。SSMS 不会立即在网格结果中显示所有 7488 行。它也只对我显示 7480。我怀疑发生这种情况是因为结果以数据包形式发送,其余 8 行不足以填充数据包。
与第一部分相比,查询的后半部分非常昂贵。查看执行计划,您可以在嵌套循环的内侧看到六个聚集索引扫描。这些扫描将对锚点部分的每一行至少执行一次。SQL Server 在几分钟内总共进行了 44928 次扫描,最终从该部分生成 0 行。一旦该部分执行完成,SQL Server 将剩余的行数据包发送到客户端,您将在已完成查询的结果网格中看到所有 7488 行。找到最后8个八行需要三分钟的时间是不正确的。它几乎可以立即找到它们。发送它们只需要三分钟。
通过查看实际计划,您可以看到在查询的下半部分执行的大量工作。请注意箭头的粗细和扫描运算符的执行次数:
这就是查询缓慢的原因。我没有任何建议让您加快速度,因为我不明白您要通过此查询完成什么。
归档时间: |
|
查看次数: |
231 次 |
最近记录: |