我有一个看起来像这样的表:
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条不对应。
我怎样才能达到这样的目标?
答案 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;
比较两个计划,您可以在最高计划中看到反半连接,但是在底部计划中有一个正常连接,后面是过滤器:
答案 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)