为什么 EXCEPTed 查询是聚簇表时执行计划中有排序运算符?
Why there is a sort operator in execution plan when EXCEPTed queries are clustered tables?
我正在构建一个数据仓库,我发现两个 tables 数据比较语句有问题。我使用 EXCEPT 运算符来比较具有聚集索引(普通 int 字段作为键)的 tables。我的问题是在查询执行计划中,在两个聚集索引扫描之后都有排序运算符。
这是一个代码示例:
create table temp.table_a
(
key_a int identity,
some_field_a int,
some_field2_a varchar(10)
);
insert into temp.table_a
(
some_field_a,
some_field2_a
)
select
n,
'abcd'
from meta.GENERATE_SEQUENCE(1,1000);
create clustered index cix_table_a_key_a on temp.table_a (key_a);
create table temp.table_b
(
key_b int identity,
some_field_b int,
some_field2_b varchar(10)
);
insert into temp.table_b
(
some_field_b,
some_field2_b
)
select
n,
'abcd'
from meta.GENERATE_SEQUENCE(1,1000);
create clustered index cix_table_b_key_b on temp.table_b (key_b);
(GENERATE_SEQUENCE 是行生成器)
现在 EXCEPT 查询:
select
key_a,
some_field_a,
some_field2_a
from temp.table_a
except
select
key_b,
some_field_b,
some_field2_b
from temp.table_b
这是执行计划的图片:
我知道 Merge Join 需要排序的输入,但它不是已经排序了吗?我的意思是,我们需要的唯一排序列是 key_a/key_b。由于聚簇索引,这已经完成了。不需要对其他列进行排序,因为在 key_a/key_b 的每个值中只有一行 - 无需排序。
所以,我的问题是:
- 为什么这种情况下聚簇索引扫描后还有排序运算符?
- 当我想使用 EXCEPT 运算符时,如何避免这些排序?
- 进行table比较的更好方法是什么(如果有的话)?
提前感谢您的回答:)
回答你的问题它还不够排序吗? - 没有。比较是在 SELECT
中的所有列上进行的,因此所有列都需要被列入排序。
我提供了 2 种可能的解决方案,一种是将所有列添加到索引中,另一种是使用 NOT EXISTS - 请注意,如果 table_a 中存在重复,这可能会 return 重复行.
1.) 在索引中包含这些列,您将在 SQLFiddle 链接查询计划中看到在这种情况下没有使用排序。
MS SQL Server 2017 架构设置:
create table table_a
(
key_a int identity,
some_field_a int,
some_field2_a varchar(10)
);
WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
insert into table_a
(
some_field_a,
some_field2_a
)
select
n,
'abcd'
from Tally;
create clustered index cix_table_a_key_a on table_a (key_a,some_field_a,
some_field2_a);
create table table_b
(
key_b int identity,
some_field_b int,
some_field2_b varchar(10)
);
WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
insert into table_b
(
some_field_b,
some_field2_b
)
select
n,
'abcd'
from Tally;
create clustered index cix_table_b_key_b on table_b (key_b, some_field_b, some_field2_b);
查询 1:
select
key_a,
some_field_a,
some_field2_a
from table_a
except
select
key_b,
some_field_b,
some_field2_b
from table_b
2.) 另一种选择是使用 NOT EXISTS
而不是 EXCEPT
- 应该注意的是,这些与 EXCEPT
有效执行 [=18] 并不完全相同=] 也是。如果将 DISTINCT
添加到 NOT EXISTS
,您将对较小的结果进行排序 table。
请在此处查看 SQLFIDLLE:
MS SQL Server 2017 架构设置:
create table table_a
(
key_a int identity,
some_field_a int,
some_field2_a varchar(10)
);
WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
insert into table_a
(
some_field_a,
some_field2_a
)
select
n,
'abcd'
from Tally;
create clustered index cix_table_a_key_a on table_a (key_a);
create table table_b
(
key_b int identity,
some_field_b int,
some_field2_b varchar(10)
);
WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
insert into table_b
(
some_field_b,
some_field2_b
)
select
n,
'abcd'
from Tally;
create clustered index cix_table_b_key_b on table_b (key_b);
查询 1:
select
key_a,
some_field_a,
some_field2_a
from table_a
WHERE NOT EXISTS
(
select NULL
from table_b
WHERE
key_b = key_a AND
some_field_b = some_field_a AND
some_field2_b = some_field2_a
)
感谢大家的帮助。以下是我从评论和答案中收集到的问题的完整答案:
- Why there are sort operators after clustered index scans in this situation?
存在排序运算符是因为索引键列(key_a、key_b)对于优化器而言不是唯一的。
- How can I avoid these sorts when I want to use EXCEPT operator?
确保您的索引键列是唯一的 - 使用 UNIQUE CLUSTERED INDEX 或对这些字段设置约束。
- What are the better ways (if there is any) of doing table comparison?
Steve Ford (@steve-ford) 的回答中给出了考虑向索引键添加更多列或使用 NOT EXISTS 而不是 EXCEPT 的替代解决方案。
我正在构建一个数据仓库,我发现两个 tables 数据比较语句有问题。我使用 EXCEPT 运算符来比较具有聚集索引(普通 int 字段作为键)的 tables。我的问题是在查询执行计划中,在两个聚集索引扫描之后都有排序运算符。 这是一个代码示例:
create table temp.table_a
(
key_a int identity,
some_field_a int,
some_field2_a varchar(10)
);
insert into temp.table_a
(
some_field_a,
some_field2_a
)
select
n,
'abcd'
from meta.GENERATE_SEQUENCE(1,1000);
create clustered index cix_table_a_key_a on temp.table_a (key_a);
create table temp.table_b
(
key_b int identity,
some_field_b int,
some_field2_b varchar(10)
);
insert into temp.table_b
(
some_field_b,
some_field2_b
)
select
n,
'abcd'
from meta.GENERATE_SEQUENCE(1,1000);
create clustered index cix_table_b_key_b on temp.table_b (key_b);
(GENERATE_SEQUENCE 是行生成器)
现在 EXCEPT 查询:
select
key_a,
some_field_a,
some_field2_a
from temp.table_a
except
select
key_b,
some_field_b,
some_field2_b
from temp.table_b
这是执行计划的图片:
我知道 Merge Join 需要排序的输入,但它不是已经排序了吗?我的意思是,我们需要的唯一排序列是 key_a/key_b。由于聚簇索引,这已经完成了。不需要对其他列进行排序,因为在 key_a/key_b 的每个值中只有一行 - 无需排序。
所以,我的问题是:
- 为什么这种情况下聚簇索引扫描后还有排序运算符?
- 当我想使用 EXCEPT 运算符时,如何避免这些排序?
- 进行table比较的更好方法是什么(如果有的话)?
提前感谢您的回答:)
回答你的问题它还不够排序吗? - 没有。比较是在 SELECT
中的所有列上进行的,因此所有列都需要被列入排序。
我提供了 2 种可能的解决方案,一种是将所有列添加到索引中,另一种是使用 NOT EXISTS - 请注意,如果 table_a 中存在重复,这可能会 return 重复行.
1.) 在索引中包含这些列,您将在 SQLFiddle 链接查询计划中看到在这种情况下没有使用排序。
MS SQL Server 2017 架构设置:
create table table_a
(
key_a int identity,
some_field_a int,
some_field2_a varchar(10)
);
WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
insert into table_a
(
some_field_a,
some_field2_a
)
select
n,
'abcd'
from Tally;
create clustered index cix_table_a_key_a on table_a (key_a,some_field_a,
some_field2_a);
create table table_b
(
key_b int identity,
some_field_b int,
some_field2_b varchar(10)
);
WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
insert into table_b
(
some_field_b,
some_field2_b
)
select
n,
'abcd'
from Tally;
create clustered index cix_table_b_key_b on table_b (key_b, some_field_b, some_field2_b);
查询 1:
select
key_a,
some_field_a,
some_field2_a
from table_a
except
select
key_b,
some_field_b,
some_field2_b
from table_b
2.) 另一种选择是使用 NOT EXISTS
而不是 EXCEPT
- 应该注意的是,这些与 EXCEPT
有效执行 [=18] 并不完全相同=] 也是。如果将 DISTINCT
添加到 NOT EXISTS
,您将对较小的结果进行排序 table。
请在此处查看 SQLFIDLLE:
MS SQL Server 2017 架构设置:
create table table_a
(
key_a int identity,
some_field_a int,
some_field2_a varchar(10)
);
WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
insert into table_a
(
some_field_a,
some_field2_a
)
select
n,
'abcd'
from Tally;
create clustered index cix_table_a_key_a on table_a (key_a);
create table table_b
(
key_b int identity,
some_field_b int,
some_field2_b varchar(10)
);
WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
insert into table_b
(
some_field_b,
some_field2_b
)
select
n,
'abcd'
from Tally;
create clustered index cix_table_b_key_b on table_b (key_b);
查询 1:
select
key_a,
some_field_a,
some_field2_a
from table_a
WHERE NOT EXISTS
(
select NULL
from table_b
WHERE
key_b = key_a AND
some_field_b = some_field_a AND
some_field2_b = some_field2_a
)
感谢大家的帮助。以下是我从评论和答案中收集到的问题的完整答案:
- Why there are sort operators after clustered index scans in this situation?
存在排序运算符是因为索引键列(key_a、key_b)对于优化器而言不是唯一的。
- How can I avoid these sorts when I want to use EXCEPT operator?
确保您的索引键列是唯一的 - 使用 UNIQUE CLUSTERED INDEX 或对这些字段设置约束。
- What are the better ways (if there is any) of doing table comparison?
Steve Ford (@steve-ford) 的回答中给出了考虑向索引键添加更多列或使用 NOT EXISTS 而不是 EXCEPT 的替代解决方案。