从SQL Server中的列派生序列

时间:2016-09-19 01:30:37

标签: sql sql-server

我的数据类似于:

ID1   ID2  ID3  ID4
123  32    43   123
56   67    56   89
123  56   123   56

基本上描述了从ID1开始到ID4结束的序列。我感兴趣的只是提取模式,而不是涉及的ID。例如,第一行中的模式为:

ABCA:因为它从ID开始,转到新ID(B),然后是另一个新ID(C),然后回到原始ID(A)。

第二行是:ABAC

而第三个则是:ABAB。

我正在寻找一种有效的方法在sql server中执行此操作,而不是为每个潜在的情况使用大量的if语句。

1 个答案:

答案 0 :(得分:3)

嗯。这是一种蛮力方法:

select 'A' +
       (case when id2 = id1 then 'A' else 'B' end) +
       (case when id3 = id1 then 'A'
             when id3 = id2 then 'B'
             when id2 = id1 then 'B'
             else 'C'
        end) +
       (case when id4 = id1 then 'A'
             when id4 = id2 then 'B'
             when id4 = id3 and id2 = id1 then 'B'
             when id3 = id2 then 'C'
             when id2 = id1 then 'C'
             else 'D'
        end)

这有点复杂,但这样的事情应该有效。

编辑:

这是另一种应该起作用的方法:

select t.*, pattern
from t outer apply(
     (select (max(case when id = 1 then val end) +
              max(case when id = 2 then val end) +
              max(case when id = 3 then val end) +
              max(case when id = 4 then val end)
             ) pattern
      from (select v.*,
                   char(ascii('A' + dense_rank() over (order by minpos) - 1)) as val
            from (select v.*, min(pos) over (partition by id) as minpos
                  from (values(id1, 1), (id2, 2), (id3, 3), (id4, 4)) as v(id, pos)
                 ) v
            ) v
      ) v;

解释这是如何工作的是一个相当大的挑战。 values()命令将数据转换为行,因此第一行最终如下:

id    pos
123     1
 32     2
 43     3
123     4

下一级将最小值置于找到值的位置:

id    pos    minpos
123     1      1
 32     2      2
 43     3      3
123     4      1

(注意:数字是连续的巧合。)

然后dense_rank()将其变成字母:

id    pos    minpos   val
123     1      1       A
 32     2      2       B
 43     3      3       C
123     4      1       A

最后的聚合将其置于ABCA模式中。