2014-02-26 56 views
4

我有一個包含實時股票報價的SQL Server數據庫。如何從數據庫中刪除幾乎相同記錄的連續序列

有包含你會expect--一個序列號,股票代碼,時間,價格,出價,出價大小,問,問大小等

該序列號對應於消息的行情表接收到的數據包含正在跟蹤的一組股票代碼的數據。每當有任何被跟蹤的符號發生變化時,就會收到一條新消息(帶有一個新的遞增順序號)。該消息包含所有符號的數據(即使沒有任何變化的數據)。

將數據放入數據庫時​​,每條消息中的每個符號都會插入一條記錄,即使對於自先前消息以來沒有任何變化的符號也是如此。所以很多記錄包含冗餘信息(只有序號改變),我想刪除這些冗餘記錄。

這與除去整個數據庫中除一條記錄以外的所有相同列的組合(已經回答)不同。相反,我想將每個相同記錄的連續塊(​​除了序列號以外相同)壓縮到單個記錄中。完成後,可能會有重複的記錄,但它們之間的記錄不同。

我的方法是找到連續的記錄範圍(對於股票代碼),其中除了序列號之外的所有內容都是相同的。

在下面的示例數據中,我通過僅顯示序列,符號和價格來簡化事情。複合主鍵爲序列+符號(每個符號在消息中只出現一次)。我想刪除價格與先前記錄相同的記錄(對於給定的股票代碼)。對於股票X這意味着我想刪除的範圍[1,6],以及用於股票YI要刪除的範圍[1,2],[4,5]和[7,7]:

之前:

Sequence Symbol Price 
    0  X  $10 
    0  Y  $ 5 
    1  X  $10 
    1  Y  $ 5 
    2  X  $10 
    2  Y  $ 5 
    3  X  $10 
    3  Y  $ 6 
    4  X  $10 
    4  Y  $ 6 
    5  X  $10 
    5  Y  $ 6 
    6  X  $10 
    6  Y  $ 5 
    7  X  $11 
    7  Y  $ 5 

後:

Sequence Symbol Price 
    0  X  $10 
    0  Y  $ 5 
    3  Y  $ 6 
    6  Y  $ 5 
    7  X  $11 

需要注意的是(Y,$ 5)出現兩次,但之間(Y,$ 6)。

以下生成我需要的範圍。左外連接確保我選擇第一組記錄(沒有先前記錄不同),而BETWEEN旨在減少需要搜索的記錄數以查找下一個較早的不同記錄(沒有BETWEEN的結果是一樣的,但速度較慢)。我只需要添加一些東西,如「DELETE FROM QUotes WHERE Sequence BETWEEN StartOfRange AND EndOfRange」。

SELECT 
    GroupsOfIdenticalRecords.Symbol, 
    MIN(GroupsOfIdenticalRecords.Sequence)+1 AS StartOfRange, 
    MAX(GroupsOfIdenticalRecords.Sequence) AS EndOfRange 
FROM 
    (
    SELECT 
     Q1.Symbol, 
     Q1.Sequence, 
     MAX(Q2.Sequence) AS ClosestEarlierDifferentRecord 
    FROM 
     Quotes AS Q1 
    LEFT OUTER JOIN 
     Quotes AS Q2 
    ON 
      Q2.Sequence BETWEEN Q1.Sequence-100 AND Q1.Sequence-1 
     AND Q2.Symbol=Q1.Symbol 
     AND Q2.Price<>Q1.Price 
    GROUP BY 
     Q1.Sequence, 
     Q1.Symbol 
    ) AS GroupsOfIdenticalRecords 
GROUP BY 
    GroupsOfIdenticalRecords.Symbol, 
    GroupsOfIdenticalRecords.ClosestEarlierDifferentRecord 

問題是,這太慢了,並且用於數據庫中的2百萬記錄的內存不足(崩潰SSMS-)。即使我將「-100」更改爲「-2」,它仍然很慢並且內存不足。我期望LEFT OUTER JOIN的「ON」子句限制處理和內存使用(200萬次迭代,每個處理大約100條記錄,這些記錄應該是可處理的),但是似乎SQL Server可能首先會生成所有的根據ON子句中指定的標準選擇表格之前的兩個實例Q1和Q2(約4個12個組合)。

如果我在較小的數據子集上運行查詢(例如,通過使用「(SELECT TOP 100000 FROM引用)AS Q1」,以及類似的Q2),它會在合理的時間內完成。我試圖弄清楚如何使用「WHERE Sequence BETWEEN 0 AND 99999」自動運行20次左右,然後「...... BETWEEN 100000 AND 199999」等(實際上我會使用重疊範圍,如[0, 99999],[99900,199999]等刪除跨越邊界的範圍)。

以下生成一組範圍,將數據拆分爲100000個記錄塊([0,99999],[100000,199999]等)。但是,如何重複應用上述查詢(每個範圍一次)?我一直陷入困境,因爲您無法使用「BETWEEN」對這些進行分組而不應用聚合函數。因此,我不知道如何選擇記錄塊,只知道如何得到不符合上述查詢(如Q1和Q2)的MIN(),MAX()等(單個值)。有沒有辦法做到這一點?這個問題有沒有完全不同的(更好的)方法?

SELECT 
    CONVERT(INTEGER, Sequence/100000)*100000 AS BlockStart, 
    MIN(((1+CONVERT(INTEGER, Sequence/100000))*100000)-1) AS BlockEnd 
FROM 
    Quotes 
GROUP BY 
    CONVERT(INTEGER, Sequence/100000)*100000 

回答

1

你可以用一個很好的小竅門來做到這一點。你想要的組可以被定義爲兩個數字序列之間的差異。按順序爲每個符號分配一個符號。另一個分配給每個符號和價格。這是什麼樣子爲您的數據:

Sequence Symbol Price seq1 seq2 diff 
    0  X  $10  1  1  0 
    0  Y  $ 5  1  1  0 
    1  X  $10  2  2  0 
    1  Y  $ 5  2  2  0 
    2  X  $10  3  3  0 
    2  Y  $ 5  3  3  0 
    3  X  $10  4  4  0 
    3  Y  $ 6  4  1  3 
    4  X  $10  5  5  0 
    4  Y  $ 6  5  2  3 
    5  X  $10  6  6  0 
    5  Y  $ 6  6  3  3 
    6  X  $10  7  7  0 
    6  Y  $ 5  7  4  3 
    7  X  $11  8  1  7 
    7  Y  $ 5  8  5  3 

您可以在此盯着弄清楚,符號,DIFF和價格的組合定義每個組。

下面放成一個SQL查詢這個返回數據,你想:

select min(q.sequence) as sequence, symbol, price 
from (select q.*, 
      (row_number() over (partition by symbol order by sequence) - 
       row_number() over (partition by symbol, price order by sequence) 
      ) as grp 
     from quotes q 
    ) q 
group by symbol, grp, price; 

如果要替換原表中的數據,我建議你存儲查詢的結果一個臨時表,截斷原始表,然後重新插入臨時表中的值。

0

回答我自己的問題。我想補充一些補充評論來補充Gordon Linoff的出色答案。

你說得對。這是一個很好的小動作。我不得不盯着它一段時間來了解它是如何工作的。這是我對其他人的好處的想法。

序列/符號(seq1)的編號總是增加,而符號/價格(seq2)的編號有時會增加(每個組內只有當Symbol的記錄包含組的價格時)。因此,seq1要麼與seq2保持同步(即diff保持不變,直到符號或價格發生變化),要麼seq1從seq2「跑開」(當它忙於「計數」其他價格和其他符號時 - 會增加對於給定的符號和價格,seq1和seq2之間的差異)。一旦seq2落後,它就永遠不會「趕上」seq1,所以一旦diff移動到下一個更大的值(對於給定的價格),diff的給定值再也不會再被看到。通過在每個符號/價格組中獲取最小值,您將獲得每個連續區塊中的第一條記錄,這正是我所需要的。

我不使用SQL很多,所以我不熟悉OVER子句。我只是相信第一個子句生成seq1,第二個生成seq2。我可以看看它是如何工作的,但這不是有趣的部分。

我的數據包含的不止是Price。這是一個簡單的事情添加其他領域(買價,賣價等)到第二OVER子句和最後GROUP BY:

row_number() over (partition by Symbol, Price, Bid, BidSize, Ask, AskSize, Change, Volume, DayLow, DayHigh, Time order by Sequence) 

group by Symbol, grp, price, Bid, BidSize, Ask, AskSize, Change, Volume, DayLow, DayHigh, Time 

另外,我是能夠使用應用> MIN(... )和< = MAX(...)來定義要刪除的記錄範圍。