CTE - LEFT OUTER JOIN 性能问题
CTE - LEFT OUTER JOIN Performance Problem
使用 SQL 服务器 2017。
SQL FIDDLE: LINK
CREATE TABLE [TABLE_1]
(
PLAN_NR decimal(28,6) NULL,
START_DATE datetime NULL,
);
CREATE TABLE [TABLE_2]
(
PLAN_NR decimal(28,6) NULL,
PERIOD_NR decimal(28,6) NULL,
);
INSERT INTO TABLE_1 (PLAN_NR, START_DATE)
VALUES (1, '2020-05-01'), (2, '2020-08-05');
INSERT INTO TABLE_2 (PLAN_NR, PERIOD_NR)
VALUES (1, 1), (1, 2), (1, 5), (1, 6), (1, 5), (1, 6), (1, 17),
(2, 2), (2, 3), (2, 5), (2, 2), (2, 17), (2, 28);
CREATE VIEW ALL_PERIODS
AS
WITH rec_cte AS
(
SELECT
PLAN_NR, START_DATE,
1 period_nr, DATEADD(day, 7, START_DATE) next_date
FROM
TABLE_1
UNION ALL
SELECT
PLAN_NR, next_date,
period_nr + 1, DATEADD(day, 7, next_date)
FROM
rec_cte
WHERE
period_nr < 100
),
cte1 AS
(
SELECT
PLAN_NR, period_nr, START_DATE
FROM
rec_cte
UNION ALL
SELECT
PLAN_NR, period_nr, DATEADD(DAY, 1, EOMONTH(next_date, -1))
FROM
rec_cte
WHERE
MONTH(START_DATE) <> MONTH(next_date)
),
cte2 AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY PLAN_NR ORDER BY START_DATE) rn
FROM cte1
)
SELECT PLAN_NR, rn PERIOD_NR, START_DATE
FROM cte2
WHERE rn <= 100
Table_1 列出计划 (PLAN_NR) 及其开始日期 (START_DATE)。
Table_2 列出计划编号 (PLAN_NR) 和期间 (1 - X)。每个计划编号期间可能会出现多次,但也可能会丢失。
一个时期持续 7 天,除非该时期包括月份的变化。然后期间分为月末前的部分和月末后的部分。
视图 ALL_PERIODS 根据此系统列出了每个计划的 100 个期间。
我的问题是以下 select 的性能,我想在视图中使用它:
SELECT
t2.PLAN_NR
, t2.PERIOD_NR
, a_p.START_DATE
from TABLE_2 as t2
left outer join ALL_PERIODS a_p on t2.PERIOD_NR = a_p.PERIOD_NR and t2.PLAN_NR = a_p.PLAN_NR
从 TABLE_2 中的大约 4000 个条目开始,select 变得非常慢。
连接本身还没有减慢查询速度。只有额外的 select a_p.START_DATE 一切都变得非常慢。
我将视图读入临时 table 并对其进行连接,没有遇到任何性能问题。 (4000 个条目需要 2 秒)。
所以我假设视图中使用的 CTE 是性能缓慢的原因。
不幸的是,我不能在视图中使用临时 tables,我不想将数据写入正常的 table.
SQL 服务器有没有办法改善 CTE 延迟?
不是递归 CTE,而是生成 ALL_PERIODS,在计划 table 和“数字 table”之间进行交叉连接,或者持续存在,或者作为非递归 CTE。
EG
WITH N As
(
select top 100 row_number() over (order by (select null)) i
from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10) ) v1(i),
(values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10) ) v2(i)
),
plan_period AS
(
SELECT
PLAN_NR, START_DATE,
N.i period_nr, DATEADD(day, 7*N.i, START_DATE) next_date
FROM TABLE_1 CROSS JOIN N
),
如果您可以修改视图,我建议您这样做:
- 添加一个 table 包含从 0 到您认为在数据库中需要的任何数字,您可以使用以下命令:
create table numbers ( id int)
go
;with cte (
select 0 num
union all
select num + 1
where num < 2000 -- change this
)
insert into number
from num from cte
- 将视图中的第一个 cte 更改为:
WITH rec_cte AS
(
SELECT
PLAN_NR
, DATEADD(DAY, 7* id, START_DATE) START_DATE
, id +1 period_nr
, DATEADD(DAY, 7*( id+1), START_DATE) next_date
FROM
TABLE_1 t
CROSS apply intenum i
WHERE i.id <100
),...
- 同时考虑使用 temp table 而不是 cte 它可能会有帮助
使用 SQL 服务器 2017。
SQL FIDDLE: LINK
CREATE TABLE [TABLE_1]
(
PLAN_NR decimal(28,6) NULL,
START_DATE datetime NULL,
);
CREATE TABLE [TABLE_2]
(
PLAN_NR decimal(28,6) NULL,
PERIOD_NR decimal(28,6) NULL,
);
INSERT INTO TABLE_1 (PLAN_NR, START_DATE)
VALUES (1, '2020-05-01'), (2, '2020-08-05');
INSERT INTO TABLE_2 (PLAN_NR, PERIOD_NR)
VALUES (1, 1), (1, 2), (1, 5), (1, 6), (1, 5), (1, 6), (1, 17),
(2, 2), (2, 3), (2, 5), (2, 2), (2, 17), (2, 28);
CREATE VIEW ALL_PERIODS
AS
WITH rec_cte AS
(
SELECT
PLAN_NR, START_DATE,
1 period_nr, DATEADD(day, 7, START_DATE) next_date
FROM
TABLE_1
UNION ALL
SELECT
PLAN_NR, next_date,
period_nr + 1, DATEADD(day, 7, next_date)
FROM
rec_cte
WHERE
period_nr < 100
),
cte1 AS
(
SELECT
PLAN_NR, period_nr, START_DATE
FROM
rec_cte
UNION ALL
SELECT
PLAN_NR, period_nr, DATEADD(DAY, 1, EOMONTH(next_date, -1))
FROM
rec_cte
WHERE
MONTH(START_DATE) <> MONTH(next_date)
),
cte2 AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY PLAN_NR ORDER BY START_DATE) rn
FROM cte1
)
SELECT PLAN_NR, rn PERIOD_NR, START_DATE
FROM cte2
WHERE rn <= 100
Table_1 列出计划 (PLAN_NR) 及其开始日期 (START_DATE)。 Table_2 列出计划编号 (PLAN_NR) 和期间 (1 - X)。每个计划编号期间可能会出现多次,但也可能会丢失。 一个时期持续 7 天,除非该时期包括月份的变化。然后期间分为月末前的部分和月末后的部分。
视图 ALL_PERIODS 根据此系统列出了每个计划的 100 个期间。
我的问题是以下 select 的性能,我想在视图中使用它:
SELECT
t2.PLAN_NR
, t2.PERIOD_NR
, a_p.START_DATE
from TABLE_2 as t2
left outer join ALL_PERIODS a_p on t2.PERIOD_NR = a_p.PERIOD_NR and t2.PLAN_NR = a_p.PLAN_NR
从 TABLE_2 中的大约 4000 个条目开始,select 变得非常慢。
连接本身还没有减慢查询速度。只有额外的 select a_p.START_DATE 一切都变得非常慢。
我将视图读入临时 table 并对其进行连接,没有遇到任何性能问题。 (4000 个条目需要 2 秒)。
所以我假设视图中使用的 CTE 是性能缓慢的原因。 不幸的是,我不能在视图中使用临时 tables,我不想将数据写入正常的 table.
SQL 服务器有没有办法改善 CTE 延迟?
不是递归 CTE,而是生成 ALL_PERIODS,在计划 table 和“数字 table”之间进行交叉连接,或者持续存在,或者作为非递归 CTE。
EG
WITH N As
(
select top 100 row_number() over (order by (select null)) i
from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10) ) v1(i),
(values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10) ) v2(i)
),
plan_period AS
(
SELECT
PLAN_NR, START_DATE,
N.i period_nr, DATEADD(day, 7*N.i, START_DATE) next_date
FROM TABLE_1 CROSS JOIN N
),
如果您可以修改视图,我建议您这样做:
- 添加一个 table 包含从 0 到您认为在数据库中需要的任何数字,您可以使用以下命令:
create table numbers ( id int)
go
;with cte (
select 0 num
union all
select num + 1
where num < 2000 -- change this
)
insert into number
from num from cte
- 将视图中的第一个 cte 更改为:
WITH rec_cte AS
(
SELECT
PLAN_NR
, DATEADD(DAY, 7* id, START_DATE) START_DATE
, id +1 period_nr
, DATEADD(DAY, 7*( id+1), START_DATE) next_date
FROM
TABLE_1 t
CROSS apply intenum i
WHERE i.id <100
),...
- 同时考虑使用 temp table 而不是 cte 它可能会有帮助