删除两个日期之间未更改的行

时间:2019-02-01 20:41:00

标签: tsql sql-server-2012

我在SQL Server中继承了一个非常大的旧表(100M行)。这是帐户和余额的每日快照,但是其中一些帐户甚至不再更改,而是每天都在添加(不要问为什么!)

我要:

  • 识别并删除未更改的行,除非进行更改

  • 创建一个查询,让他们在需要删除的行时就给我,好像它们仍然在

我有一个可以使用的日期维度表。

这将生成当前表:

CREATE TABLE #Account_Snapshot(
[Snapshot_Id] [int] NOT NULL,
[Snapshot_Date] [date] NOT NULL,
[Account] [nvarchar](20) NOT NULL,
[Balance] [decimal](18, 2) NOT NULL,
CONSTRAINT [PK_Account_Snapshot_1] PRIMARY KEY CLUSTERED 
(
    [Snapshot_Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = 
OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

INSERT INTO #Account_Snapshot VALUES(1, '2019-01-01', '1', 1505.31)
INSERT INTO #Account_Snapshot VALUES(2, '2019-01-01', '2', 2337.48)
INSERT INTO #Account_Snapshot VALUES(3, '2019-01-01', '3', 1088.07)

INSERT INTO #Account_Snapshot VALUES(4, '2019-02-01', '1', 1505.31)
INSERT INTO #Account_Snapshot VALUES(5, '2019-02-01', '2', 2132.17)
INSERT INTO #Account_Snapshot VALUES(6, '2019-02-01', '3', 1088.07)

INSERT INTO #Account_Snapshot VALUES(7, '2019-03-01', '1', 1505.31)
INSERT INTO #Account_Snapshot VALUES(8, '2019-03-01', '2', 2132.17)
INSERT INTO #Account_Snapshot VALUES(9, '2019-03-01', '3', 749.23)

SELECT * FROM #Account_Snapshot
ORDER BY  Account, Snapshot_Date


Snapshot_Id Snapshot_Date Account              Balance
----------- ------------- -------------------- ---------------------
1           2019-01-01    1                    1505.31
4           2019-02-01    1                    1505.31
7           2019-03-01    1                    1505.31
2           2019-01-01    2                    2337.48
5           2019-02-01    2                    2132.17
8           2019-03-01    2                    2132.17
3           2019-01-01    3                    1088.07
6           2019-02-01    3                    1088.07
9           2019-03-01    3                    749.23

我需要一个删除逻辑,用于标识未更改的行并首先删除它们:

DELETE FROM #Account_Snapshot WHERE Snapshot_Id IN (4,6,7,8)

SELECT * FROM #Account_Snapshot
ORDER BY  Account, Snapshot_Date

Snapshot_Id Snapshot_Date Account              Balance
----------- ------------- -------------------- --------------------
1           2019-01-01    1                    1505.31
2           2019-01-01    2                    2337.48
5           2019-02-01    2                    2132.17
3           2019-01-01    3                    1088.07
9           2019-03-01    3                    749.23

然后,当表较小时,我需要进行查询以创建一个可能使用日期维表的视图,以便能够即时生成结果。

3 个答案:

答案 0 :(得分:1)

这是一个使用LAG()获取帐户先前余额的选项,可以在子查询中使用该选项,然后可以过滤余额等于先前余额(基本上没有变化)的位置以获取所需的记录删除:

--Show what records to remove
SELECT *
FROM   (
           SELECT *
                , LAG([Balance], 1, 0) OVER ( PARTITION BY [Account]
                                              ORDER BY [Snapshot_Date]
                                            ) AS [PreBalance]
           FROM   [#Account_Snapshot]
       ) AS [p]
WHERE  [p].[Balance] = [p].[PreBalance];

--Then just to delete them
DELETE p
FROM   (
           SELECT *
                , LAG([Balance], 1, 0) OVER ( PARTITION BY [Account]
                                              ORDER BY [Snapshot_Date]
                                            ) AS [PreBalance]
           FROM   [#Account_Snapshot]
       ) AS [p]
WHERE  [p].[Balance] = [p].[PreBalance];

删除后,要像以前一样表示数据,是的,您需要一个日历表,从该表中构建一个“帐户/日期”表(所有帐户和所有日期的组合),然后交叉应用回原始表以进行填充在天平上。

示例:

--Quick sample calendar table
INSERT INTO [#cal] (
                       [CalDate]
                   )
VALUES ('2019-01-01')
     , ('2019-02-01')
     , ('2019-03-01');

--So this would get us a full list of all accounts and dates based on you calendar table
SELECT     *
FROM       [#cal] [a]
INNER JOIN (
               SELECT DISTINCT [Account]
               FROM   [#Account_Snapshot]
           ) [b]
    ON 1 = 1;

结果为:

CalDate     Account
2019-01-01  1
2019-02-01  1
2019-03-01  1
2019-01-01  2
2019-02-01  2
2019-03-01  2
2019-01-01  3
2019-02-01  3
2019-03-01  3

然后在子查询中使用它并交叉应用回帐户表以填充余额:

SELECT      *
FROM        (
                SELECT     *
                FROM       [#cal] [a]
                INNER JOIN (
                               SELECT DISTINCT [Account]
                               FROM   [#Account_Snapshot]
                           ) [b]
                    ON 1 = 1
            ) AS [AcctCal]
CROSS APPLY (
                SELECT   TOP 1 [acct].[Balance]
                FROM     [#Account_Snapshot] [acct]
                WHERE    [acct].[Account] = [AcctCal].[Account]
                         AND [acct].[Snapshot_Date] <= [AcctCal].[CalDate]
                ORDER BY [acct].[Snapshot_Date] desc
            ) AS [bal];

为您提供原始结果:

CalDate     Account Balance
2019-01-01  1       1505.31
2019-02-01  1       1505.31
2019-03-01  1       1505.31
2019-01-01  2       2337.48
2019-02-01  2       2132.17
2019-03-01  2       2132.17
2019-01-01  3       1088.07
2019-02-01  3       1088.07
2019-03-01  3       749.23

答案 1 :(得分:0)

要确定未更改的行,我们可以在CTE中使用带有分区的row_number,如下所示:

;with cte as (
  select *
 ,ROW_NUMBER() over(partition by [Account],[Balance] order by [Account],Snapshot_Date,[Balance]) [Row]
from #Account_Snapshot 
),DataFiltered  as (select * from cte where cte.[Row]=1  --not a duplicate
),DataDuplicate as (select * from cte where cte.[Row]>1  --any number larger than one is a duplicate
)
   select * from DataFiltered
   ORDER BY  Account, Snapshot_Date

结果:-

Snapshot_Id Snapshot_Date   Account Balance Row
1           2019-01-01      1       1505.31 1
2           2019-01-01      2       2337.48 1
5           2019-02-01      2       2132.17 1
3           2019-01-01      3       1088.07 1
9           2019-03-01      3       749.23  1

要获得重复的数据,我们可以在最后两行更改为如下: -

select * from DataDuplicate
ORDER BY  Account, Snapshot_Date

结果:-

Snapshot_Id Snapshot_Date   Account Balance Row
4           2019-02-01          1   1505.31 2
7           2019-03-01          1   1505.31 3
8           2019-03-01          2   2132.17 2
6           2019-02-01          3   1088.07 2

要删除重复的数据,我们将最后两行替换为:-

delete from #Account_Snapshot where Snapshot_Id in (
select Snapshot_Id  from DataDuplicate)

我希望这会有所帮助。

答案 2 :(得分:0)

尝试一下:

;with cte as (
select *,ROW_NUMBER() over (partition by balance order by Snapshot_Id) rn from #Account_Snapshot
)

Delete from cte where rn > 1