0

我有一个记录会话和事件的数据库。事件表包括时间戳和用户名。一个用户可能是匿名的,或者在任何给定的会话期间登录了许多不同的用户。

我正在尝试根据事件时间戳和用户名更改时创建登录会话标识符。我可以根据时间戳对事件进行排序,并且可以看到用户名何时更改,但我无法弄清楚如何将每个事件行与其各自的“登录会话”相关联,该“登录会话”标识用户名更改时的最新事件。

我创建了这个示例来模拟我需要做的事情。a.ord是连续序列中的一个序数,a.val是一个值。b.pval是系列中的前一个值。

with a as (
    select 1 AS ord, 'abc' as val union
    select 2, 'xyz' union
    select 3, 'abc' union
    select 4, 'abc' union
    select 5, 'xyz' union
    select 6, 'xyz' union
            select 7, 'xyz'
),
b as (
    select
        a1.ord as ord,
        a1.val as val,
        a2.val as pval
    from a a1
    left join a a2 on a1.ord - 1 = a2.ord 
    where a1.val != isnull(a2.val, '')
)
select
    *
from b

上述代码返回值发生变化的行,包括值从null变为“abc”时的第一行。这是返回的内容:

ORD VAL PVAL
1   abc NULL
2   xyz abc
3   abc xyz
5   xyz abc

我需要以某种方式返回a left join b,以便它返回以下内容,其中“ORD”是 a.ord,“VAL”是 a.val,“CHG”是 b.ord。

ORD VAL CHG
1   abc 1
2   xyz 2
3   abc 3
4   abc 3
5   xyz 5
6   xyz 5
7   xyz 5

谢谢你的帮助!

4

2 回答 2

2

使用 Itzik Ben-Gan 对岛屿和间隙的解决方案(由 Mikael Eriksson通过回答这个 SO question向我提出)变得非常简单。从包含与两个序列相同的数字的列grouped中检索所有记录。这个数字的组合是独一无二的。在主查询中,选择按这两个值分组的 min(ord)。agrpval

这是带有示例的 Sql Fiddle

; with a as (
    select 1 AS ord, 'abc' as val union all
    select 2, 'xyz' union all
    select 3, 'abc' union all
    select 4, 'abc' union all
    select 5, 'xyz' union all
    select 6, 'xyz' union all
    select 7, 'xyz'
),
grouped as (
    select *,
           row_number() over (order by ord)
         - row_number() over (order by val, ord) grp
      from a
)
select ord, 
       val, 
       min(ord) over(partition by grp, val) chg
  from grouped
 order by ord

和一些阅读材料

于 2012-09-06T20:04:52.353 回答
0

我在尝试为这个问题写一个可读的标题时发现了这一点。

答案如下:

with a as (
    select 1 AS ord, 'abc' as val union
    select 2, 'xyz' union
    select 3, 'abc' union
    select 4, 'abc' union
    select 5, 'xyz' union
    select 6, 'xyz' union
    select 7, 'xyz'
),
b as (
    select
        a1.ord ord,
        a1.val val,
        a2.val sval
    from a a1
    left join a a2 on a1.ord - 1 = a2.ord 
    where a1.val != isnull(a2.val, '')
)
select
    *
from a
outer apply (
    select
        MAX(ord) as chg
    from b
    where a.val = b.val
    and a.ord >= b.ord
) x
于 2012-09-06T18:26:42.173 回答