我有一个包含价格历史记录的非常大表。
CREATE TABLE [dbo].[SupplierPurchasePrice](
[SupplierPurchasePriceId] [int] IDENTITY(1,1) PRIMARY KEY,
[ExternalSupplierPurchasePriceId] [varchar](20) NULL,
[ProductId] [int] NOT NULL,
[SupplierId] [int] NOT NULL,
[Price] [money] NOT NULL,
[PreviousPrice] [money] NULL,
[SupplierPurchasePriceDate] [date] NOT NULL,
[Created] [datetime] NULL,
[Modified] [datetime] NULL,
)
每件产品(Id)和供应商(Id)我有数百条价格记录。 现在需要删除大量数据,但仍保留一些历史数据。对于每个产品(Id)和供应商(Id),我想保留,比方说,14条记录。但不是第一个或最后一个14.我想保留第一个和最后一个记录。然后在第一个和最后一个之间均匀地保留12条记录。这样我就保持了一些历史。
我无法找到一种方法直接使用存储过程而不是通过我的c#ORM(这太慢了)。
答案 0 :(得分:3)
这是解决问题的直接计数方法:
select spp.*
from (select spp.*,
sum(12.5 / (cnt - 1)) over (partition by SupplierId, ProductId
order by SupplierPurchasePriceId
) as cum
from (select spp.*,
row_number() over (partition by SupplierId, ProductId
order by SupplierPurchasePriceId
) as seqnum,
count(*) over (partition by SupplierId, ProductId) as cnt,
from SupplierPurchasePrice spp
) spp
) spp
where seqnum = 1 or seqnum = cnt or cnt <= 14 or
(floor(cumgap) <> floor(cumgap - 12.5/(cnt - 1)));
挑战在于决定两者之间的12条记录。这会计算记录中的平均“差距”,为12.5/(cnt - 1)
。这是一个常数,然后在记录上累积。它将从最大记录中的基本0到12.5。想法是抓取任何传递整数值的记录。因此,如果累积形式为2.1到2.3,则不选择记录。如果它从2.9变为3.1,则选择记录。
12.5的数字并不神奇。 12到13之间的任何数字都应该这样。除了选择最旧和最新值的问题。我选择12.5以确保这些不计入12。
你可以在SQL Fiddle看到同样的逻辑工作here。标志列显示将选择哪个,并且totflag验证正好选择了14个。
答案 1 :(得分:1)
我会尝试像
这样的东西select
to_keep.SupplierPurchasePriceId
from
(select
foo.SupplierPurchasePriceId,
row_number() over (partition by ProductId, SupplierId, tile_num order by Created) as takeme
from
(select
SupplierPurchasePriceId,
ProductId,
SupplierId,
Created,
ntile(13) over(partition by ProductId, SupplierId order by Created) as tile_num
from
SupplierPurchasePrice
) foo
) to_keep
where
to_keep.takeme = 1
union
select distinct
last_value(SupplierPurchasePriceId) over (partition by ProductId, SupplierId order by Created range between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING) as SupplierPurchasePriceId
from
SupplierPurchasePrice
这应该保留行的主键。表现可能有所不同未经测试。
答案 2 :(得分:1)
(警告 - 我错误地读了你的问题。这保留了中间的第一个,最后一个和第14个记录,不一定总共有14个记录。)
备份数据库后,首先尝试干运行,检查这是否会删除所需的数据(此处假设记录的时间顺序由SupplierPurchasePriceDate
设置 - 更改{{1}如果不是这样的话,则视情况而定)
ORDER BY
如果这按预期工作,并且因为您似乎已经有代理键,那么删除步骤只是删除匹配的代理键:
WITH CTE AS
(
SELECT
[SupplierId],
[ProductId],
[SupplierPurchasePriceId],
[SupplierPurchasePriceDate],
ROW_NUMBER() OVER (Partition BY [SupplierId], [ProductId]
ORDER BY [SupplierPurchasePriceDate]) -1 AS Rnk
FROM [dbo].[SupplierPurchasePrice]
)
SELECT cteRank.*
FROM
CTE cteRank
JOIN
(SELECT ProductId, SupplierId, MAX(Rnk) as MaxRnk
FROM CTE cteMax
GROUP BY ProductId, SupplierID) X
ON cteRank.SupplierId = X.SupplierId AND cteRank.ProductId = X.ProductId
WHERE cteRank.Rnk % 12 != 0 AND cteRank.Rnk != X.MaxRnk;
它的工作原理是按日期对价格数据进行排序,然后删除不是模12的记录(根据您的要求进行更改)。 -1是因为DELETE FROM [dbo].[SupplierPurchasePrice]
WHERE [SupplierPurchasePriceId] IN (...)
是1。保留第一条记录是因为第一条Modulo匹配。 ROW_NUMBER()
步骤还要保留每个MaxRnk
,Supplier
对的最新价格。
请注意,这将保留第一个,之后每12个,最后一个。因此,最新(最大)和最后一个记录(最后一个Modulo = 0)之间可能存在不均衡的差距。但当然,足够接近?