C# 确定表中记录的特定序列

C# 确定表中记录的特定序列,c#,sql,sql-server,sql-server-2005,C#,Sql,Sql Server,Sql Server 2005,假设一个表包含TransactionId、ItemId、Code、EffectiveDate和CreateDate字段 +---------------+--------+------+------------------+------------------+ | TransactionId | ItemId | Code | EffectiveDate | CreateDate | +---------------+--------+------+-------------

假设一个表包含TransactionId、ItemId、Code、EffectiveDate和CreateDate字段

+---------------+--------+------+------------------+------------------+ | TransactionId | ItemId | Code | EffectiveDate | CreateDate | +---------------+--------+------+------------------+------------------+ | 1| 1| 8| 12/2/2009 1:13 PM| 12/2/2009 1:13 PM| +---------------+--------+------+------------------+------------------+ | 4| 1| 51|12/2/2009 11:08 AM| 12/3/2009 9:01 AM| +---------------+--------+------+------------------+------------------+ | 2| 1| 14|12/2/2009 11:09 AM|12/2/2009 11:09 AM| +---------------+--------+------+------------------+------------------+ | 3| 1| 61| 12/3/2009 8:33 AM| 12/3/2009 8:33 AM| +---------------+--------+------+------------------+------------------+ | 5| 1| 28| 12/3/2009 9:33 AM| 12/3/2009 9:33 AM| +---------------+--------+------+------------------+------------------+ | 6| 1| 9| 12/3/2009 1:58 PM| 12/3/2009 1:58 PM| +---------------+--------+------+------------------+------------------+ +---------------+--------+------+------------------+------------------+ |交易ID |项目ID |代码|生效日期|创建日期| +---------------+--------+------+------------------+------------------+ |1 | 1 | 8 | 12/2/2009下午1:13 | 12/2/2009下午1:13| +---------------+--------+------+------------------+------------------+ |4 | 1 | 51 | 12/2/2009 11:08 AM | 12/3/2009 9:01 AM| +---------------+--------+------+------------------+------------------+ |2 | 1 | 14 | 12/2/2009 11:09 AM | 12/2/2009 11:09 AM| +---------------+--------+------+------------------+------------------+ |3 | 1 | 61 | 12/3/2009上午8:33 | 12/3/2009上午8:33| +---------------+--------+------+------------------+------------------+ |5 | 1 | 28 | 12/3/2009上午9:33 | 12/3/2009上午9:33| +---------------+--------+------+------------------+------------------+ |6 | 1 | 9 | 12/3/2009下午1:58 | 12/3/2009下午1:58| +---------------+--------+------+------------------+------------------+ 我需要获得一组记录,其中序列51、61、9出现在给定的ItemId中,按EffectiveDate排序。在这些记录之间可能有其他带有其他代码的记录

在本例中,我将返回TransactionId的4、3和6,如下所示

+---------------+--------+------+------------------+------------------+ | TransactionId | ItemId | Code | EffectiveDate | CreateDate | +---------------+--------+------+------------------+------------------+ | 4| 1| 51|12/2/2009 11:08 AM| 12/3/2009 9:01 AM| +---------------+--------+------+------------------+------------------+ | 3| 1| 61| 12/3/2009 8:33 AM| 12/3/2009 8:33 AM| +---------------+--------+------+------------------+------------------+ | 6| 1| 9| 12/3/2009 1:58 PM| 12/3/2009 1:58 PM| +---------------+--------+------+------------------+------------------+ +---------------+--------+------+------------------+------------------+ |交易ID |项目ID |代码|生效日期|创建日期| +---------------+--------+------+------------------+------------------+ |4 | 1 | 51 | 12/2/2009 11:08 AM | 12/3/2009 9:01 AM| +---------------+--------+------+------------------+------------------+ |3 | 1 | 61 | 12/3/2009上午8:33 | 12/3/2009上午8:33| +---------------+--------+------+------------------+------------------+ |6 | 1 | 9 | 12/3/2009下午1:58 | 12/3/2009下午1:58| +---------------+--------+------+------------------+------------------+ 请注意:

  • 这不是我需要识别的唯一序列,但它说明了问题所在。
  • 记录可以按顺序插入到表中;也就是说,可以先插入61条记录,然后插入51条记录,然后插入9条记录。您可以在示例中看到,对于代码51记录,CreateDate晚于EffectiveDate。
  • 序列的顺序很重要。因此,序列61,9,51不会返回任何记录,但51,61,9会返回任何记录。
如果DB方法简单(即没有游标或过于复杂的存储过程),那么它是理想的,但是代码方法也可以工作,尽管它会导致大量数据从DB中传输出去


环境是SQL Server 2005和C#/.NET 3.5。

这只是我的想法,尚未经过测试,因此可能需要进行一些调整:

SELECT DISTINCT
     T.TransactionID,
     T.ItemID,
     T.Code,
     T.EffectiveDate,
     T.CreateDate
FROM
     My_Table T
INNER JOIN (
     SELECT
          T1.TransactionID,
          T2.TransactionID,
          T3.TransactionID
     FROM
          My_Table T1
     INNER JOIN My_Table T2 ON
          T2.ItemID = T1.ItemID AND
          T2.Code = 61 AND
          T2.EffectiveDate > T1.EffectiveDate
     INNER JOIN My_Table T3 ON
          T3.ItemID = T1.ItemID AND
          T3.Code = 9 AND
          T3.EffectiveDate > T2.EffectiveDate
     WHERE
          T1.Code = 51
     ) SQ ON
     SQ.TransactionID = T1.TransactionID OR
     SQ.TransactionID = T2.TransactionID OR
     SQ.TransactionID = T3.TransactionID
如果DB方法简单(即没有游标或过于复杂的存储过程),则它是理想的方法

我不相信纯DB方法(“纯”意味着只使用SQL选择)是实用的,因为我设想的SQL类型需要非常复杂的自连接、字段连接、MAX()函数等。这种SQL可能是Joe Celko的“智能SQL”中一个难题的有趣的学术答案但我认为这不适合生产代码

我认为现实的方法是编写某种跟踪状态的循环。一般来说,您的问题与编写代码对TCPIP数据包进行状态检查以过滤垃圾邮件或扫描信用卡交易中的欺诈模式非常相似。所有这些问题都具有相似的特征:您对当前行(记录)采取的操作取决于您以前看到的记录(上下文)。。。这方面需要保存状态变量


如果您希望避免对数据进行往返分析,Transact-SQL似乎是提高性能的最佳方法。或者使用托管CLR来利用C#语法,同时仍将处理保持在数据库引擎中。

实际上,您可以利用and/Or and获得两个相当简单的解决方案

创建一个过程,该过程接受在所需序列中查找的基于字符的逗号分隔的代码值列表-使用由序列和代码值组成的任何一个,生成一个具有如下结构的表:

declare @sequence table (sequence int not null, Code int not null);
with srcData as (
    select  row_number() over(order by t.EffectiveDate) as rn,
            t.TransactionId, t.ItemId, t.Code, t.EffectiveDate, t.CreateDate
    from    #TableName t
    join    @sequence s
    on      t.Code = s.Code
    where   t.ItemId = @item_id
)
select  d.TransactionId, d.ItemId, d.Code, d.EffectiveDate, d.CreateDate
from    srcData d
join    @sequence s
on      d.rn = s.sequence
and     d.Code = s.Code
order by d.rn;
一旦你有了这个,你只需要根据给定ItemId的相同代码值将序列表连接到源表,然后对源集进行排序-一旦你对源集进行了过滤和排序,你就可以根据匹配的序列值再次连接-这听起来很复杂,但在现实中,这将是一个单一的查询,如下所示:

declare @sequence table (sequence int not null, Code int not null);
with srcData as (
    select  row_number() over(order by t.EffectiveDate) as rn,
            t.TransactionId, t.ItemId, t.Code, t.EffectiveDate, t.CreateDate
    from    #TableName t
    join    @sequence s
    on      t.Code = s.Code
    where   t.ItemId = @item_id
)
select  d.TransactionId, d.ItemId, d.Code, d.EffectiveDate, d.CreateDate
from    srcData d
join    @sequence s
on      d.rn = s.sequence
and     d.Code = s.Code
order by d.rn;
单凭这一点并不能保证您得到的结果集与您正在寻找的结果集完全相同,但将数据暂存到临时表中并在代码周围添加一些简单的检查就可以做到这一点(例如,添加校验和验证以及代码值的和)

如果您希望以内联方式完成所有操作,那么可以使用另一种方法,利用CTE和递归执行运行的求和/求和以及类似OrdPath的比较(尽管您仍然需要将序列字符数据解析到数据集中)


显然,这在某种意义上是有限的,因为您正在硬编码要查找的序列数量,并且在缩放要提取的序列数量时需要继续附加子查询/联接。