SQL Server:将当前行与之前的所有行进行比较

时间:2014-11-27 17:31:51

标签: sql-server tsql

我有一张包含以下数据的表格,按姓名和日期排序

Row Name   Pos  Date
--- ----   ---- ----
1   Anne    A   11/01/2014
2   Anne    A   11/02/2014
3   Anne    C   11/04/2014
4   Anne    B   11/06/2014
5   Anne    C   11/08/2014
6   Jane    A   11/01/2014
7   Jane    A   11/02/2014
8   Jane    C   11/03/2014
9   Mark    B   11/01/2014
10  Mark    A   11/04/2014
11  Mark    A   11/06/2014
12  Mark    B   11/07/2014
13  Mark    C   11/08/2014

规则是用户不应该在C之前至少他/她已经A两次。一旦用户进入C,他/她在A之前必须再次C两次或更多次。您如何找到遵循此规则的用户?

对于此示例,答案应为JaneMarkAnne违反了第5行的规则。

3 个答案:

答案 0 :(得分:3)

假设您的表名为Records,以下是利用SQL Server 2012的LAG window function进行操作的方法:

首先,让我们找到违反规则的用户:

;with ACRecords as (
  select Row, Name, Date, Pos
  from Records
  where Pos in ('A', 'C')
),
AC3ConsecutiveRecords as (
    select
    Name,
    Pos,
    lag(Pos, 1) over (partition by Name order by Date) as LastPos,
    lag(Pos, 2) over (partition by Name order by Date) as SecondToLastPos
    from ACRecords
)
select 
    Name
from AC3ConsecutiveRecords
where Pos = 'C' and (LastPos <> 'A' or SecondToLastPos <> 'A')

此查询执行以下操作:

  1. 过滤掉除{A或C}以外的Pos值的记录。
  2. 从第1点获得的数据集中,为每条记录选择PosLastPosSecondToLastPos,我们使用LAG(使用适当的{{ {1}}和PARTITION条款。)
  3. 从第2点获得的数据集中,选择ORDER BY等于C且前两个Pos值不同于A的行(至少其中一个)。为每个记录选择名称。这些人违反了规则。
  4. 为了让那些遵守规则的人,只需将最后一步包裹在另一个CTE中,然后选择除违反规则的人以外的所有人:

    Pos

    编辑:

    SQLFiddle的SQL 2012实例目前已关闭,但幸运的是查询在Postgres中正常运行,因此这里有一个实时版本:http://sqlfiddle.com/#!15/bed67/1

答案 1 :(得分:0)

让我们设置场景:

declare @table table(row int,name varchar(9),pos char(1),date smalldatetime)
insert @table values(1,'Anne','A','20141101')
insert @table values(2,'Anne','A','20141102')
insert @table values(3,'Anne','C','20141104')
insert @table values(4,'Anne','B','20141106')
insert @table values(5,'Anne','C','20141108')
insert @table values(6,'Jane','A','20141101')
insert @table values(7,'Jane','A','20141102')
insert @table values(8,'Jane','C','20141103')
insert @table values(9,'Mark','B','20141101')
insert @table values(10,'Mark','A','20141104')
insert @table values(11,'Mark','A','20141106')
insert @table values(12,'Mark','B','20141107')
insert @table values(13,'Mark','C','20141108')

下面的查询给出了预期的结果:

select distinct a.name 
from @table a 
where a.pos = 'C'
-- 1. ensure that there are no C's which is followed by less than 2 A's
and not exists (
    select * from @table b where a.name = b.name and b.pos = 'C' and b.date < a.date
    and (select count(*) from @table c where a.name = c.name and c.pos = 'A' and c.date < a.date and c.date > b.date) < 2) 
-- 2. ensure that there are no C pairs which have less than 2 A's between them
and not exists (
    select * from @table d join @table e on d.name = e.name
    where d.pos = 'C' and e.pos = 'C' and d.name = a.name and d.date < e.date
    and (select count(*) from @table f where f.name = a.name and f.pos = 'A' and f.date < e.date and f.date > d.date) < 2) 

答案 2 :(得分:-1)

我认为唯一的解决方案是创建一个触发器而不是insert来检查这个

(从插入中选择count(pos),其中pos = C)&lt; =(从插入中选择2 * count(pos),其中pos = A)

并根据结果回滚或提交