TSQL检查是否存在特定的行序列

时间:2012-03-16 14:08:46

标签: sql sql-server sql-server-2005 tsql

我有一个包含字段的表:

History_ID   ORDER_ID   Previous_STATUS_ID   Next_STATUS_ID
0            2          null                 1
1            2          1                    2
2            2          2                    3
3            1          null                 1
4            2          3                    4
5            3          null                 2
6            2          4                    1
7            2          1                    2
8            2          2                    5
9            7          3                    4

10 4 6 2    11 9 3 5

它包含我的订单IDID状态。基本上,表格包含每个订单的STATUS历史记录。

我想检查特定订单是否有特定的订单序列。

示例:如果订单ID 4具有状态2,4,3,1,则返回true

状态应该是一个接一个,所以在2之后必须是4,4之后是3之后,之后是1。

这些序列向我提供有关特定订单流程的信息(将在我的报告中显示)。

我需要检查那种序列:

  • 1,2,3
  • 1,4,3,2
  • 4,2
  • (1或2或3或4),5,3-那些4中的一个然后是5,然后是3

我不知道如何从这种查询开始:/

编辑:
我的最终报告是一个包含有关订单信息的表格:

Orders type  Count      ...
Profile1     145        ...
Profile2     217        ...
Profile3     12         ...
Profile4     2          ...

我有大约800k +订单,我的报告必须每天完成,所以改为为整个表做报告(检查那些状态并从其他表汇总信息)我决定创建一个报告表:

Order_ID   Profile   Order_date   Customer_profile     ...
1          5        '2008-01-03'  2
4          1        '2009-04-10'  3
3          7        '2011-11-03'  1
4523       2        '2012-11-03'  5

这样我就可以创建一个夜间作业,用我的信息向这个表添加新订单,我只是从该表中做一个简单的选择,以避免聚合。 第一次插入会很大,但在第二天晚上它只会添加新订单。

例如,查看ID = 2的订单 Next_STATUS_ID就像这样:1,2,3,4,1,2,5 ...我想检查这个订单是否有1,2,5等历史记录更改,所以我的函数(select语句)应该重复1,如果它按顺序查找这些ID。

2 个答案:

答案 0 :(得分:1)

我认为这可以使用递归CTE来解决:

with change_tree as 
(
  SELECT order_id,
         previous_status_id, 
         next_status_id,
         cast(next_status_id as varchar(max)) as status_path
  FROM status_change
  WHERE previous_status_id = 1
    AND order_id = 2

  UNION ALL 

  SELECT sc.order_id,
         sc.previous_status_id,
         sc.next_status_id,
         ct.status_path + ',' + cast(sc.next_status_id as varchar(max))
  FROM status_change sc 
    JOIN change_tree ct ON ct.next_status_id = sc.previous_status_id AND ct.order_id = sc.order_id
)
SELECT *
FROM change_tree
WHERE status_path = '2,3,5';

这基本上是(递归地)将next_status_id的所有值连接到可以比较的单个字符串。

从您的示例数据中,我不清楚哪一行标记订单状态更改历史记录的“开始”。您需要调整联合第一部分的位置以选择正确的“起始”行。

答案 1 :(得分:0)

我可能这样做:

  1. 创建一个包含序列测试的存储过程

    一个。按顺序ID将行按行以历史ID的顺序选择到本地表@variable,并使用带有种子1和增量1的IDENTITY列进行升序

    湾你接下来做什么取决于你想要的复杂/花哨/可维护性。一个快速而肮脏的解决方案是按顺序从表变量构建一个以逗号分隔的ID列表,并将它们与一些硬编码的序列字符串进行比较。

    来自多行的several techniques for building comma-delimited strings

    ℃。对于更漂亮的解决方案,您可以创建一个新表(类似于“HistoryTestSequence”),看起来像这样(以说明您的1,4,3,2测试):

    TestNumber | SequenceOrder | CompareValue
    -----------+---------------+-------------
             1 |             1 |            1
             1 |             2 |            4
             1 |             3 |            3
             1 |             4 |            2
    

    然后,您可以从表变量尝试JOIN,将标识列与SequenceOrder列匹配,将历史标识与CompareValue列匹配,WHERE TestNumber = 1。如果在JOIN之后返回的COUNT()行等于COUNT(*) FROM HistoryTestSequence WHERE TestNumber = 1,那么您已经匹配了序列。您只需重复测试每组可能的序列,您可以在循环中或手动完成。

  2. 如果我有更多的时间,我会喜欢拼凑一个例子,但我现在就让你试一试。后一个例子可能有点过分。

    最后,请记住,对身份列进行测试是危险的 - 一台服务器上的序列创建的ID可能在另一台服务器上具有不同的ID(例如,如果您有生产和UAT SQL服务器实例稍微不在同步)。