我在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
然后,当表较小时,我需要进行查询以创建一个可能使用日期维表的视图,以便能够即时生成结果。
答案 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