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
),

如果您可以修改视图,我建议您这样做:

  1. 添加一个 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
  1. 将视图中的第一个 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       
  ),...
  1. 同时考虑使用 temp table 而不是 cte 它可能会有帮助