Sql 如何从数据库中删除几乎相同记录的连续序列

Sql 如何从数据库中删除几乎相同记录的连续序列,sql,sql-server,Sql,Sql Server,我有一个SQL Server数据库,其中包含实时股票报价 有一个报价表,包含您期望的内容-序列号、股票代码、时间、价格、出价、出价大小、询问、询问大小等 序列号对应于接收到的包含跟踪的一组股票代码数据的消息。每当所跟踪的任何符号发生任何变化时,都会收到一条带有新的递增序列号的新消息。该消息包含所有符号的数据,即使这些符号没有更改 将数据放入数据库时,会为每条消息中的每个符号插入一条记录,即使是自上一条消息以来未发生任何更改的符号也是如此。所以很多记录包含冗余信息,只有序列号改变了,我想删除这些冗

我有一个SQL Server数据库,其中包含实时股票报价

有一个报价表,包含您期望的内容-序列号、股票代码、时间、价格、出价、出价大小、询问、询问大小等

序列号对应于接收到的包含跟踪的一组股票代码数据的消息。每当所跟踪的任何符号发生任何变化时,都会收到一条带有新的递增序列号的新消息。该消息包含所有符号的数据,即使这些符号没有更改

将数据放入数据库时,会为每条消息中的每个符号插入一条记录,即使是自上一条消息以来未发生任何更改的符号也是如此。所以很多记录包含冗余信息,只有序列号改变了,我想删除这些冗余记录

这与从整个数据库中删除已应答的相同列组合的所有记录(只有一条记录)不同。相反,我想将除了序列号之外的相同记录的每个连续块压缩到单个记录中。完成后,可能会有重复的记录,但它们之间的记录不同

我的方法是为一个股票代码找到连续的记录范围,其中除了序列号之外,所有内容都是相同的

在下面的示例数据中,我通过只显示序列、符号和价格来简化事情。复合主键为Sequence+Symbol每个符号在消息中仅出现一次。我想删除价格与给定股票代码的先前记录相同的记录。对于ticker X,它意味着我要删除范围[1,6],对于ticker Y,我要删除范围[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介于两者之间

下面生成了我需要的范围。左外连接确保我选择第一组记录,其中没有不同的早期记录,中间连接旨在减少需要搜索的记录数,以找到下一个早期不同的记录。没有中间连接,结果相同,但速度较慢。我只需要添加一些类似于从引号中删除的内容,其中包含StartOfRange和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

你可以用一个漂亮的小把戏来做到这一点。所需的组可以定义为两个数字序列之间的差。按顺序为每个符号分配一个。另一个为每个符号和价格指定。这是您的数据的外观:

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
您可以仔细观察这一点,发现符号、差异和价格的组合定义了每个组

以下内容将其放入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;

如果要替换原始表中的数据,我建议您将查询结果存储在临时表中,截断原始表,然后重新插入临时表中的值。

回答我自己的问题。我想补充一下Gordon Linoff的精彩回答

你说得对。这是一个很好的小把戏。我不得不盯着它看了一会儿才明白它是如何工作的。以下是我对他人的看法

按顺序/符号seq1的编号始终增加,而按符号/价格seq2的编号仅在每个组内有时增加,仅当符号的记录包含组的价格时。因此,seq1或与seq2保持同步,即差异保持不变,直到符号或价格发生变化,或seq1在忙于计算其他价格和其他符号时离开seq2,这增加了给定符号和价格的seq1和seq2之间的差异。一旦seq2落后,它就永远赶不上seq1,因此,一旦diff在给定价格下移动到下一个较大的值,就再也看不到给定的diff值。通过获取每个符号/价格组中的最小值,可以得到每个连续块中的第一条记录,这正是我所需要的

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

我的数据包含的不仅仅是价格。通过以下方式将其他字段Bid、Ask等添加到第二个OVER子句和最后一组中是一件简单的事情:

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
此外,我还可以使用>分钟。。。和
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