我观察到排序UNION
查询中的选择列表对性能有显著的影响。
UNION
我处理的查询的一般形式是:
SELECT * FROM (
SELECT <select_list> FROM <table>
UNION ALL
...
) q
ORDER BY <column>
使用外部选择是因为它在存在的情况下显著提高了性能ORDER BY
,但这超出了本问题的范围。
UNION ALL
总是使用,而不是UNION
。
我将把性能称为“快”(瞬间)或“慢”(5 秒或更长时间)。
通过在 DBeaver 控制台中运行查询来测量性能,默认情况下仅检索第一页,其大小为 200。
更改选择列表会以下列方式影响性能:
- 当
<select_list> = <column>
(即,仅选择排序列)时,查询速度很快。执行计划显示排序列的索引扫描和连接的合并连接。 - 当选择列表除了排序列外还包含其他列时,查询可能会很快,也可能会很慢。据观察,当以下条件全部满足时,查询会很快:
- 选择列表包括聚集索引列。
- 选择列表以聚集索引列开始,或者以排序列和紧接着聚集索引列的序列开始。
使用 Microsoft SQL Server 2019 (RTM-CU26) (KB5035123) - 15.0.4365.2 (X64) 进行观察。
UNION
我在 SQL Server 文档中找不到有关选择列表对性能的影响的任何信息。
下面给出了进行实验的环境和查询本身的简单描述。
CREATE TABLE AUDIT1 (
ID bigint NOT NULL,
AUDITDATE datetime2 NULL,
[USER] bigint NULL,
-- Implies clustered index.
CONSTRAINT PK_AUDIT1 PRIMARY KEY (ID)
);
CREATE INDEX I_AUDIT1_AUDITDATE ON AUDIT1 (AUDITDATE);
CREATE TABLE AUDIT2 (
ID bigint NOT NULL,
AUDITDATE datetime2 NULL,
[USER] bigint NULL,
-- Implies clustered index.
CONSTRAINT PK_AUDIT2 PRIMARY KEY (ID)
);
CREATE INDEX I_AUDIT2_AUDITDATE ON AUDIT2 (AUDITDATE);
- 表
AUDIT1
包含 1000 万条记录。 - 表
AUDIT2
包含 100 万条记录。 AUDITDATE
中的值AUDIT2
大于 中的值AUDIT1
。- 的值与按递增顺序
AUDITDATE
排列,即总是在递增, 也是如此。ID
ID
AUDITDATE
查询 1:选择列表仅包含排序列(快速)。
SELECT * FROM (
SELECT AUDITDATE FROM AUDIT2
UNION ALL
SELECT AUDITDATE FROM AUDIT1
) q
ORDER BY AUDITDATE
执行计划:
|--Merge Join(Concatenation)
|--Index Scan(AUDIT2.I_AUDIT2_AUDITDATE), ORDERED BACKWARD
|--Index Scan(AUDIT1.I_AUDIT1_AUDITDATE), ORDERED BACKWARD
查询 2:选择大小 > 1 的列表,包含排序列,不包含聚集索引列(慢)。
SELECT * FROM (
SELECT [USER], AUDITDATE FROM AUDIT2
UNION ALL
SELECT [USER], AUDITDATE FROM AUDIT1
) q
ORDER BY AUDITDATE
请注意,首先放置排序列似乎没有任何效果。
查询 3:选择大小 > 1 的列表,包含排序列,包含聚集索引列,第一列既不是排序列,也不是聚集索引列(慢)。
SELECT * FROM (
SELECT [USER], ID, AUDITDATE FROM AUDIT2
UNION ALL
SELECT [USER], ID, AUDITDATE FROM AUDIT1
) q
ORDER BY AUDITDATE
查询 4:选择大小 > 1 的列表,包含排序列,包含聚集索引列,第一列是排序列,或者是聚集索引列(快速)。
SELECT * FROM (
SELECT ID, [USER], AUDITDATE FROM AUDIT2
UNION ALL
SELECT ID, [USER], AUDITDATE FROM AUDIT1
) q
ORDER BY AUDITDATE
这个查询表明,如果第一列是ID
,则查询速度很快。
查询 2、3、4 的执行计划相同:
|--Parallelism(Gather Streams, ORDER BY:([Union1007] ASC))
|--Sort(ORDER BY:([Union1007] ASC))
|--Concatenation
|--Parallelism(Distribute Streams, RoundRobin Partitioning)
|--Clustered Index Scan(OBJECT:(AUDIT2.PK_AUDIT2))
|--Clustered Index Scan(OBJECT:(AUDIT1.PK_AUDIT1))