【问题标题】:MSSQL: Select max date when a specific column was updatedMSSQL:选择更新特定列时的最大日期
【发布时间】:2020-01-02 14:58:51
【问题描述】:

我需要返回更新了某个列的最后一条记录。 数据存储在 2 个表中:ClientInfo 和 ClientInfoHistory 两者都有大量数据,尤其是历史记录(它跟踪主表 ClientInfo 上的任何更改)。即使在 ClientInfo 中只更新了一个列,具有该更新之前版本的行将被插入到 ClientInfoHistory 中,其中包含审计信息(用户和更新该行的日期)。

所以我有兴趣返回每个 ClientID 更改 Flag3 的最大日期 + 更改该日期的用户和 Flag3 的值

整个脚本应该在一个存储过程中,以避免性能问题。

我的想法: 我使用整个客户的历史记录 + 实际数据创建一个临时表,然后比较 Flag3 以查看每个客户的该标志何时更改,并将其与 modifiedbyuserid 和 modifieddate 一起放入另一个临时表中。然后我为每个客户选择 max(modifiedDate)。

表格如下所示:

create table dbo.ClientInfo(
    ClientID int,
    Flag1 bit,
    Flag2 bit,
    Flag3 bit,
    CreatedByUserID int not null,
    CreatedDate datetime not null default getdate(),
    ModifiedByUserID int not null,
    ModifiedDate datetime not null default getdate(),
    constraint PK_UserInfo primary key (ClientID)
    )

create table dbo.ClientInfoHistory(
    ClientInfoHistoryID bigint identity not null,
    ClientID int,
    Flag1 bit,
    Flag2 bit,
    Flag3 bit,
    CreatedByUserID int not null,
    CreatedDate datetime not null,
    HistoryStartModifiedByUserID int not null,
    HistoryStartDate datetime not null,
    HistoryEndModifiedByUserID int not null,
    HistoryEndDate datetime not null,
    constraint PK_ClientInfoHistory primary key (ClientInfoHistoryID)
    )
    CREATE NONCLUSTERED INDEX ix_ClientInfoHistory_ClientID_HistoryDates
    on dbo.ClientInfoHistory (ClientID, HistoryStartDate, HistoryEndDate)

包含整个客户历史记录 + 实际数据的临时表包含以下内容:

    SELECT c.ClientID
     ,c.Flag3
     ,c.ModifiedDate as HistoryStartDate
     ,'9999-12-31' as HistoryEndDate
     ,c.ModifiedByUserId as HistoryStartModifiedByUserID
    FROM dbo.ClientInfo c  with (nolock) 

UNION 

     SELECT
          ch.ClientID
         ,ch.Flag3
         ,ch.HistoryStartDate
         ,ch.HistoryEndDate
         ,ch.HistoryStartModifiedByUserID
     FROM  dbo.ClientInfoHistory ch with (nolock)
    insert into dbo.ClientInfo (ClientID, Flag1, Flag2, Flag3, CreatedByUserID, CreatedDate, ModifiedByUserID, ModifiedDate)
    values
    (1,0,0,1,1234,'2019-05-29 04:57:13.360', 1111,'2019-07-01 08:35:13.360'),
    (2,0,1,0,1233,'2019-03-12 04:57:13.360', 1233, '2019-03-12 04:57:13.360')


    insert into dbo.ClientInfoHistory (ClientID, Flag1, Flag2, Flag3, CreatedByUserID, CreatedDate, HistoryStartModifiedByUserID,HistoryStartDate, HistoryEndModifiedByUserID, HistoryEndDate )
    values
    (1,1,1,1,1234,'2019-05-29 04:57:13.360', 1234, '2019-05-29 04:57:13.360', 4321,'2019-05-31 04:57:13.360'),
    (1,0,1,1,1234,'2019-05-29 04:57:13.360', 4321,'2019-05-31 04:57:13.360', 9871,'2019-06-02 06:27:13.360'),
    (1,0,1,0,1234,'2019-05-29 04:57:13.360',9871,'2019-06-02 06:27:13.360', 8765,'2019-06-21 11:06:13.360'),
    (1,0,0,0,1234,'2019-05-29 04:57:13.360',8765,'2019-06-21 11:06:13.360', 9871,'2019-06-22 06:27:13.360')
    (1,0,0,1,1234,'2019-05-29 04:57:13.360',9871,'2019-06-22 06:27:13.360', 4321,'2019-06-25 08:35:13.360'),
    (1,0,1,1,1234,'2019-05-29 04:57:13.360',4321,'2019-06-25 08:35:13.360', 1111,'2019-07-01 08:35:13.360')

所以例如上面...那个标志的变化是:

ClientID,Flag3,ModifiedByUserID,ModifiedDate

  • 1、1、1234、29/05/2019
  • 1, 0, 9871, 02/06/2019
  • 1, 1, 9871, 22/06/2019***
  • 2、0、1233、12/03/2019***

最终结果集应仅返回标有 *** 的那些行(每个 ClienID 的 Flag3 的最新版本)

我对我选择的整个过程不太满意,我正在寻找另一种性能更好的解决方案。我很高兴听到你们的一些想法

【问题讨论】:

    标签: sql-server performance tsql


    【解决方案1】:

    以下查询必须给您预期的结果

    
    SELECT *
    FROM  (
            SELECT *, ROW_NUMBER () OVER (PARTITION BY ch.ClientID ORDER BY ch.HistoryStartDate DESC) as Rn
            FROM
                (
                SELECT c.ClientID
                 ,c.Flag3
                 ,c.ModifiedDate as HistoryStartDate
                 ,'9999-12-31' as HistoryEndDate
                 ,c.ModifiedByUserId as HistoryStartModifiedByUserID
                -- ,ROW_NUMBER () OVER (PARTITION BY c.ClientID ORDER BY c.ModifiedDate DESC) as Rn
                FROM dbo.ClientInfo c  with (nolock) 
    
                UNION
    
                SELECT
                      ch.ClientID
                     ,ch.Flag3
                     ,ch.HistoryStartDate
                     ,ch.HistoryEndDate
                     ,ch.HistoryStartModifiedByUserID
                      --,ROW_NUMBER () OVER (PARTITION BY ch.ClientID ORDER BY ch.HistoryStartDate DESC) as Rn
                 FROM  dbo.ClientInfoHistory ch with (nolock)
                ) as CH
        ) AS T
    WHERE T.Rn = 1
    

    【讨论】:

    • 非常感谢您的快速回复。我试图避免 row_number,因为它会降低查询性能 + 您发布的查询没有返回正确的 ModifiedDate for ClientID=1,ModifiedDate 应该是 2019-06-22 06:27:13.360 而不是 2019-06-25 08 :35:13.360
    【解决方案2】:

    您的问题并不完全清楚,但我相信这就是您要找的。 您可能希望稍微更改索引以包含 Flag3,并且根据数据的大小,您可能会考虑对临时表进行索引。但这是一个快速而肮脏的查询,可以帮助您入门

    ;WITH CTE AS
    (select t2.ClientID,  MAX(HistoryStartDate) NotMatchDate
    from #ClientInfo t1
    INNER JOIN #ClientInfoHistory t2 on t1.ClientID = t2.ClientID and t1.Flag3 <> t2.Flag3
    GROUP BY t2.ClientID
    )
    select t2.ClientID,  MIN(HistoryStartDate) ModifiedDate
    INTO #tmpClient
    from #ClientInfo t1
    INNER JOIN #ClientInfoHistory t2 on t1.ClientID = t2.ClientID and t1.Flag3 = t2.Flag3
    INNER JOIN CTE t3 on t3.ClientID = t1.ClientID
    WHERE t2.HistoryStartDate > t3.NotMatchDate
    GROUP BY t2.ClientID
    
    
    SELECT t1.ClientID, t1.Flag3,t1.HistoryStartModifiedByUserID ModifiedByUserID, t1.HistoryStartDate ModifiedDate
    FROM #ClientInfoHistory t1
    INNER JOIN #tmpClient t2 on t1.ClientID = t2.ClientID and t1.HistoryStartDate = t2.ModifiedDate
    UNION ALL
    SELECT t1.ClientID, Flag3,CreatedByUserID ModifiedByUserID,CreatedDate ModifiedDate
    FROM #ClientInfo t1
    LEFT JOIN #tmpClient t2 on t1.ClientID = t2.ClientID
    WHERE t2.ClientID IS NULL
    

    编辑 #2:已针对新行修复

    【讨论】:

    • 感谢您的回复 :) 我刚刚检查了您的脚本,但 #tempClient 不包含 CliendID=1 的正确 ModifiedDate。那个人在 2019-06-22 06:27:13.360 更改了 Flag3,此数据在历史表中。至于 ClientID=2,我们在历史表中没有任何数据,所以我们需要返回 ClientInfo 表中的内容。我正在寻找一种更好的方法来选择数据,操作更少,性能更好。
    • hmmm...我正在尝试重新表述我的问题。我想返回为每个 ClientID 更改 Flag3 的最大日期。在我的示例中,这将是:ClientID= 1, Flag3=1,ModifiedByUserId= 9871, ModifiedDate=22/06/2019*** ClientID=2, Flag3=0, ModifiedByUserId=1233, ModifiedDate= 12/03/2019* **
    • 我查看了错误的更改记录。现在应该可以按预期工作了。
    • 如果我们将 Flag2=0 更新为 0 并插入到历史表中,您的查询结果是不正确的。更新 ClientInfo set flag2=0, modifiedDate='2019-07-01 08:35:13.360', modifiedbyuserid=1111 where clientid=1 insert into dbo.ClientInfoHistory (ClientID, Flag1, Flag2, Flag3, CreatedByUserID, CreatedDate, HistoryStartModifiedByUserID,HistoryStartDate , HistoryEndModifiedByUserID, HistoryEndDate ) 值 (1,0,1,1,1234,'2019-05-29 04:57:13.360',4321,'2019-06-25 08:35:13.360', 1111,'2019- 07-01 08:35:13.360')
    • 对不起...这很痛苦 :)) 为了更好的可视化,请检查我的问题的正文。我刚刚更新了脚本
    猜你喜欢
    • 2019-02-09
    • 1970-01-01
    • 2016-10-29
    • 1970-01-01
    • 2012-12-24
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多