【问题标题】:Recursive CTE have performace issue, need suggestion to optimize query递归 CTE 存在性能问题,需要建议以优化查询
【发布时间】:2019-12-13 08:52:36
【问题描述】:

我想从日志表中获取前 5 条记录,其中“批准日期”已更改为 NULL,反之亦然。日期值无关紧要,但顺序很重要。

ApprovedDate            ChangeDate                         changeByUser
NULL                    2019-12-09 06:40:15.437              vaisakh
NULL                    2019-12-09 06:42:31.563             vaisakh
NULL                    2019-12-09 06:42:33.140             vaisakh
NULL                    2019-12-09 07:03:54.660              vaisakh
2019-12-09 07:05:29.800 2019-12-09 07:05:29.817              vaisakh
2019-12-09 07:05:29.800 2019-12-09 07:05:38.707              vaisakh
NULL                    2019-12-09 07:09:33.160               vaisakh
NULL                    2019-12-09 07:09:42.440               vaisakh
NULL                    2019-12-09 09:38:19.757             vaisakh
2019-12-09 09:41:42.977 2019-12-09 09:41:43.243             Raveendran        

在这种情况下,我想要第一条记录和第 5 条记录(有人批准了数据,这就是为什么一个值),然后第 7 条记录值是 null 有人拒绝了它。

我尝试使用递归 CTE,它可以工作,但对于大型记录,性能问题很大


DECLARE @today DATETIME = GETDATE();

with RESULT (CIPApprovedDate,ChangeDate,changeByUser,legalId,depth)AS(

  SELECT TOP 1 CIPApprovedDate, ChangeDate, changeByUser, legalId,1
  FROM LegalEntityExtensionLog
  WHERE legalId= 2688518
  ORDER BY ChangeDate ASC

  union ALL

  select 

  L.CIPApprovedDate, L.ChangeDate, L.changeByUser, L.legalId,ct.depth+1
  FROM LegalEntityExtensionLog L INNER JOIN Result CT
 on L.legalId=CT.legalId AND L.changeDate>CT.changeDate

 AND ISNULL(L.CIPApprovedDate,@today) <> ISNULL(CT.CIPApprovedDate,@today) 

)select * from Log  where  ChangeDate in(select MIN(ChangeDate) from Result group by depth)

【问题讨论】:

  • 您的示例数据似乎缺少(至少)列LegalId
  • 我忘了在示例数据中添加它。我无法编辑问题。 LegalID 是恒定的。在这里你可以把它当作整行的 22222。

标签: sql sql-server tsql window-functions gaps-and-islands


【解决方案1】:

您可以像这样提取转换记录:

select ApprovedDate, ChangeDate, changeByUser
from (
    select 
        l.*,
        lag(ApprovedDate) ver(partition by LegalId order by ChangeDate) lagApprovedDate
    from LegalEntityExtensionLog l
) t
where 
    (lagApprovedDate is null and ApprovedDate is not null)
    or (lagApprovedDate is not null and ApprovedDate is null)

这将显示ApprovedDatenull 转换为非null 值(或相反)的记录。

【讨论】:

    【解决方案2】:

    通过每次递归访问表的递归 CTE 有时会很慢。

    但是是否需要获取 null/unnull 开关?

    这将得到相同的结果

    -- Sample data
    CREATE TABLE LegalEntityExtensionLog
    (
      Id int identity(1,1) primary key,
      ApprovedDate datetime, 
      ChangeDate datetime not null, 
      ChangeByUser varchar(42) not null, 
      LegalId int not null
    );
    insert into LegalEntityExtensionLog
    (ApprovedDate, ChangeDate, ChangeByUser, LegalId) values
     (NULL,                      '2019-12-09 06:40:15.437', 'vaisakh', 2688518)
    ,(NULL,                      '2019-12-09 06:42:31.563', 'vaisakh', 2688518)
    ,(NULL,                      '2019-12-09 06:42:33.140', 'vaisakh', 2688518)
    ,(NULL,                      '2019-12-09 07:03:54.660', 'vaisakh', 2688518)
    ,('2019-12-09 07:05:29.800', '2019-12-09 07:05:29.817', 'vaisakh', 2688518)
    ,('2019-12-09 07:05:29.800', '2019-12-09 07:05:38.707', 'vaisakh', 2688518)
    ,(NULL,                      '2019-12-09 07:09:33.160', 'vaisakh', 2688518)
    ,(NULL,                      '2019-12-09 07:09:42.440', 'vaisakh', 2688518)
    ,(NULL,                      '2019-12-09 09:38:19.757', 'vaisakh', 2688518)
    ,('2019-12-09 09:41:42.977', '2019-12-09 09:41:43.243', 'Raveendran', 2688518)
    ;
    

    查询:

    WITH RESULT AS
    (
        SELECT *
        , ROW_NUMBER() OVER (PARTITION BY LegalId ORDER BY ChangeDate) AS rn
        , LAG(ApprovedDate) OVER (PARTITION BY LegalId ORDER BY ChangeDate) AS prevApprDt
        FROM LegalEntityExtensionLog
        WHERE legalId = 2688518
          AND ChangeDate >= cast('2019-12-09' AS DATE)
    )
    SELECT ApprovedDate, ChangeDate, ChangeByUser, LegalId
    FROM RESULT
    WHERE 
    ( RN = 1
      OR (ApprovedDate IS NULL AND prevApprDt IS NOT NULL)
      OR (ApprovedDate IS NOT NULL AND prevApprDt IS NULL)
    )
    ORDER BY ChangeDate
    GO
    

    结果:

    批准日期 |更改日期 |按用户更改 |合法身份 :----------------- | :----------------- | :----------- | ------: | 2019 年 9 月 12 日 06:40:15 |维萨赫| 2688518 2019 年 9 月 12 日 07:05:29 | 2019 年 9 月 12 日 07:05:29 |维萨赫| 2688518 | 2019 年 9 月 12 日 07:09:33 |维萨赫| 2688518 2019 年 9 月 12 日 09:41:42 | 2019 年 9 月 12 日 09:41:43 |拉文德兰 | 2688518

    db小提琴here

    【讨论】:

    • 我想要值发生变化的记录,例如 NULL 到 DATE,然后 DATE 到 NULL。我想要上面示例数据中的第 1、5、7 行数据正在发生变化,因为有人对那些记录采取了行动,为什么批准日期更改为 DATE 然后为 NULL。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多