SQL - 用于验证连续性的查询

时间:2014-10-24 13:00:39

标签: sql sql-server tsql

我有一个看起来像这样的表:

  Id  |  PersonId  |    Date    |  Number  |  NumberOld  
------+------------+------------+----------+-----------
   1  |     1      |  2014 1 1  |    1     |      0      
   2  |     1      |  2014 1 2  |    2     |      1      
   3  |     1      |  2014 1 3  |    3     |      2      
   4  |     2      |  2014 1 1  |    1     |      0      
   5  |     2      |  2014 1 2  |    3     |      2      
   6  |     2      |  2014 1 3  |    4     |      3      

我想要的是一个查询,它会让我找到数字和数字的连续性的人。

因此对于PersonId = 1,一切都没问题,所以查询不应该返回person1。

1 - 0
2 - 1
3 - 2

但是对于PersonId = 2,没有给出数字的连续性

1 - 0
3 - 2
4 - 3

第一条记录的第1号与第二条记录的第2条不对应。

我怎样才能达到这样的目标?

5 个答案:

答案 0 :(得分:2)

我认为您可以使用left join执行此操作以查找不匹配项:

select t.person
from table t left join
     table tnext
     on t.person = tnext.person and t.number = tnext.numberold
where tnext.person is null
group by t.person
having count(*) > 1;

join查找数字不匹配的所有行。但是,对于给定的人,总会有至少一个(最后一个)。这是使用having子句过滤掉的。

答案 1 :(得分:2)

您可以使用LAG查看上一条记录。因此,您发现不匹配,并且您发现PersonIDs选择了人员的历史:

select *
from mytable
where personid in
(
  select personid
  from
  (
    select personid, numberold, lag(number) over (partition by personid order by logdate, number) as numberbefore
    from mytable
  ) lookup
  where numberold <> numberbefore
)
order by personid, logdate, number;

这是一个SQL小提琴:http://sqlfiddle.com/#!6/991e7/2

答案 2 :(得分:2)

我倾向于使用NOT EXISTS vs LEFT JOIN/IS NULL,因为作为一般规则it performs better in SQL Server

SELECT  *
FROM    T AS t1
WHERE   NOT EXISTS
        (   SELECT  1
            FROM    T AS t2
            WHERE   t1.PersonID = t1.PersonID
            AND     t2.NumberOld = t1.Number
        );

它可能对您的情况没有任何影响,但可能性能提升的原因是SQL Server无法将LEFT JOIN/IS NULL方法优化为反半连接,也就是说它将返回过滤掉空值之前的所有结果,而NOT EXISTS将在找到匹配后立即停止搜索。上面链接的文章更详细,但为了证明您的查询,我创建了下表:

CREATE TABLE #T 
(
    ID INT IDENTITY(1, 1), 
    PersonId INT, 
    Date DATE,
    Number INT, 
    NumberOld INT
);
INSERT #T (PersonID, Date, Number, NumberOld) 
VALUES 
    (1, '2014-01-01', 1, 0), (1, '2014-01-02', 2, 1), (1, '2014-01-03', 3, 2),
    (2, '2014-01-01', 1, 0), (2, '2014-01-02', 3, 2), (2, '2014-01-03', 4, 3);

然后运行了两个查询:

SELECT  *
FROM    #T AS t1
WHERE   NOT EXISTS
        (   SELECT  1
            FROM    #T AS t2
            WHERE   t1.PersonID = t1.PersonID
            AND     t2.NumberOld = t1.Number
        );

SELECT  t1.*
FROM    #T AS t1
        LEFT JOIN #T AS t2
            ON  t1.PersonID = t1.PersonID
            AND t2.NumberOld = t1.Number
WHERE   t2.PersonID IS NULL;

比较两个计划,您可以在最高计划中看到反半连接,但是在底部计划中有一个正常连接,后面是过滤器:

enter image description here

答案 3 :(得分:1)

下面的方法通过LEFT OUTER JOIN找到任意数量的人的所有空白。

DECLARE @Data TABLE (PersonId INT, Number INT, NumberOld INT)
INSERT @Data VALUES (1, 1, 0), (1, 2, 1), (1, 3, 2),
    (2, 1, 0), (2, 3, 2), (2, 4, 3)

SELECT DISTINCT
    D1.PersonId
FROM @Data D1
    LEFT OUTER JOIN @Data D2
        ON D1.Number = D2.NumberOld
WHERE D2.PersonId IS NULL

产量

PersonId
-----------
2

答案 4 :(得分:0)

这样的事情怎么样?

http://sqlfiddle.com/#!6/645a6/1/0

一个人的行数应该等于最大数量和最小旧数量之差

小提琴不起作用......这是sql

SELECT p.PersonID
FROM Person p
GROUP BY p.PersonID
HAVING COUNT(1) <> MAX(Number)-MIN(NumberOld)