SQL Server 2012清理一组数据

时间:2014-01-25 13:01:31

标签: sql sql-server sql-server-2012

我有一个包含价格历史记录的非常大表。

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(这太慢了)。

3 个答案:

答案 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()步骤还要保留每个MaxRnkSupplier对的最新价格。

请注意,这将保留第一个,之后每12个,最后一个。因此,最新(最大)和最后一个记录(最后一个Modulo = 0)之间可能存在不均衡的差距。但当然,足够接近?

SqlFiddle here