7 performance sql-server partitioning sql-server-2014 query-performance
I have tables which are partitioned based on a INT column.
I see some queries that are using $Partition function to compare partition number instead of comparing the actual field data.
For example instead of saying:
select *
from T1
inner join T2 on T2.SnapshotKey = T1.SnapshotKey
Run Code Online (Sandbox Code Playgroud)
they have been written like below:
select *
from T1
inner join T2 on $Partition.PF_Name(T2.SnapshotKey) = $Partition.PF_Name(T1.SnapshotKey)
Run Code Online (Sandbox Code Playgroud)
where PF_Name is the name of partition function.
我看到对这些查询的评论说这样做是为了提高性能,当我运行这两个查询时,我看到执行时间和执行计划不同。我不确定这两个查询有何不同。
这是一个真正的查询:
-- this takes about 9 seconds
select sum(PrinBal)
from fpc
inner join gl on gl.SnapshotKey = fpc.SnapshotKey
where fpc.SnapshotKey = 201703
-- this takes about 5 seconds
select sum(PrinBal)
from fpc
inner join gl on $Partition.SnapshotKeyPF(gl.SnapshotKey) = $Partition.SnapshotKeyPF(fpc.SnapshotKey)
where fpc.SnapshotKey = 201703
Run Code Online (Sandbox Code Playgroud)
下面是真实查询的执行计划:
抱歉,我什至无法上传经过消毒的执行计划,因为上传是由我们的网络监控的,这可能违反了政策。
问题是:为什么执行计划不同,为什么第二个查询更快。
如果有人可以分享任何想法,我很感激。只是想知道他们为什么不同。不过越快越好。
这发生在 SQL Server 2014 上。如果我在 SQL Server 2012 上运行相同的程序,结果会有所不同,第一个查询的执行速度会更快!
为什么执行计划不同
select sum(PrinBal)
from fpc
inner join gl on gl.SnapshotKey = fpc.SnapshotKey
where fpc.SnapshotKey = 201703
Run Code Online (Sandbox Code Playgroud)
优化器知道:
gl.SnapshotKey = fpc.SnapshotKey; 和fpc.SnapshotKey = 201703所以它可以推断:
gl.SnapshotKey = 201703就像你写的一样:
select sum(PrinBal)
from fpc
inner join gl on gl.SnapshotKey = fpc.SnapshotKey
where fpc.SnapshotKey = 201703
and gl.SnapshotKey = 201703
Run Code Online (Sandbox Code Playgroud)
优化器也可以使用字面值 201703 来确定分区 ID。对于两个SnapshotKey谓词(一个给定,一个推断),这意味着优化器知道两个表的分区 ID。
更进一步,SnapshotKey现在可以在两个表上使用文字值 (201703) ,连接谓词:
gl.SnapshotKey = fpc.SnapshotKey简化为:
201703 = 201703; 或者干脆true这意味着根本没有连接谓词。结果是逻辑交叉连接。使用最接近的可用 T-SQL 语法表达最终执行计划,就好像你写了:
SELECT
CASE
WHEN SUM(Q1.c) = 0 THEN NULL
ELSE SUM(Q1.s)
END
FROM
(
SELECT c = COUNT_BIG(*), s = SUM(GL.PrinBal)
FROM dbo.gl AS GL
WHERE GL.SnapshotKey = 201703
AND $PARTITION.PF(GL.SnapshotKey) = $PARTITION.PF(201703)
) AS Q1
CROSS JOIN
(
SELECT Dummy = 1
FROM dbo.fpc AS FPC
WHERE FPC.SnapshotKey = 201703
AND $PARTITION.PF(FPC.SnapshotKey) = $PARTITION.PF(201703)
) AS Q2;
Run Code Online (Sandbox Code Playgroud)
select sum(PrinBal)
from fpc
inner join gl on $Partition.PF(gl.SnapshotKey) = $Partition.PF(fpc.SnapshotKey)
where fpc.SnapshotKey = 201703
Run Code Online (Sandbox Code Playgroud)
优化器无法再推断出关于 的任何信息gl.SnapshotKey,因此不再可能对第一个查询进行简化和转换。
实际上,除非每个分区确实只包含一个SnapshotKey,否则不能保证重写会产生相同的结果。
同样,使用最接近的可用 T-SQL 语法表达生成的执行计划:
SELECT
CASE
WHEN SUM(Q2.c) = 0 THEN NULL
ELSE SUM(Q2.s)
END
FROM
(
SELECT
Q1.PtnID,
c = COUNT_BIG(*),
s = SUM(Q1.PrinBal)
FROM
(
SELECT GL.PrinBal, PtnID = $PARTITION.PF(GL.SnapshotKey)
FROM dbo.gl AS GL
) AS Q1
GROUP BY
Q1.PtnID
) AS Q2
CROSS APPLY
(
SELECT Dummy = 1
FROM dbo.fpc AS FPC
WHERE
$PARTITION.PF(FPC.SnapshotKey) = Q2.PtnID
AND FPC.SnapshotKey = 201703
) AS Q3;
Run Code Online (Sandbox Code Playgroud)
这次没有逻辑交叉连接。相反,分区 id 上有一个相关的连接(一个应用)。
为什么第二个查询更快。
从所提供的信息很难评估这一点。使用基于提供的查询和计划图像的模拟数据和表,我发现第一个查询在每种情况下都优于第二个查询。
使用不同语法表达的相同查询通常会产生不同的执行计划,这仅仅是因为优化器从不同的点开始,并在找到合适的执行计划之前以不同的顺序探索选项。计划搜索并非详尽无遗,并且并非所有可能的逻辑转换都可用,因此最终结果可能会有所不同。如上所述,无论如何,这两个查询不一定表达相同的要求(至少考虑到优化器可用的信息)。
另外请注意,SQL Server 2012(以及在较小程度上,2014)中的初始列存储实现有许多限制,尤其是在优化方面。通过升级到更新版本(最好是最新版本),您可能会获得更好、更一致的结果。如果您要使用分区,则尤其如此。
我当然不会建议你养成使用 重写连接的习惯$PARTITION,除非作为最后的手段,并且对你正在做的事情有非常深入的了解。
这就是我在无法看到架构或计划细节的情况下所能说的。