2014-01-25 59 views
3

我有一個非常大表包含價格歷史。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(這太慢了)。

+2

你可以添加sql小提琴嗎? –

+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

我會嘗試像

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 

這應該給行的主鍵來保持。性能可能不盡相同未經測試。

1

(警告 - 我讀了你的問題不正確,這一保留了第一,最後在,不一定共有14條記錄之間每隔14日記錄。)

備份數據庫,並試圖模擬運行後這首先要檢查,這將刪除所需要的數據(由SupplierPurchasePriceDate這裏假定的是,記錄的時間順序設置 - 改變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; 

如果按預期工作,和因爲看起來你已經有了代理鍵,那麼刪除步驟就是deleti納克匹配的代理鍵:

DELETE FROM [dbo].[SupplierPurchasePrice] 
WHERE [SupplierPurchasePriceId] IN (...) 

它通過排名日期的價格數據,然後刪除未模12(更改按照您的要求)的記錄。 -1是因爲ROW_NUMBER()是1。第一條記錄被保留,因爲第一條Modulo匹配。步驟MaxRnk也是爲每個SupplierProduct對保留最新價格。

請注意,這將保留第一個,此後每隔12分鐘和最後一個。所以最近(最大)和倒數第二個記錄(最後一個Modulo = 0)之間可能會有不均勻的差距。但是,確實足夠接近?