我有一个表格,其中包含“丢失”和“找到”项目的条目。每行都有一个事件的日期。我希望通过将表连接到自身来构建具有匹配的'itemid','丢失日期','找到日期'的查询。
这很有用:不幸的是,如果给定项目有多个丢失和找到的对,则每个“丢失日期”将与其后的所有“找到日期”相结合。
还在我身边吗?
查询类似于:
select c0.ItemId, c0.ChangeDate, c1.ChangeDate from Changes c0
join Changes c1 on
c0.ItemId = c1.ItemId and c1.ChangeDate >= c0.ChangeDate
where c0.ChangeType = 9 (lost) and c1.ChangeType = 10 (found);
我希望实现的是给定“丢失日期”的某种形式,只与序列中的下一个“找到日期”配对(如果不存在“找到日期”,则为NULL)。我(很)确定这是可能的,但我没有看到路径。
我想知道在第一次连接中放置一个子选择并使用LIMIT 1只获得一条记录但是我没有看到如何将它连接到select主要部分的相应行。 MySQL告诉我它不存在。很公平。
答案 0 :(得分:3)
通常在处理日期对时(例如,调度的开始/结束),建议不要将它们放在单独的行上。将它们放在同一行的两列中。请参阅Joe Celko's SQL Programming Style。
但是说,您可以通过搜索另一个自联接来搜索两者之间的ChangeDate,从而使用当前架构解决它。如果没有找到(也就是说,如果c2。*由于外连接而为空),那么c0和c1是“相邻的”。
select c0.ItemId, c0.ChangeDate, c1.ChangeDate
from Changes c0
inner join Changes c1 on
c0.ItemId = c1.ItemId and c1.ChangeDate > c0.ChangeDate
left outer join Changes c2 on
c0.ItemId = c2.ItemId and c2.ChangeDate > c0.ChangeDate
and c2.ChangeDate < c1.ChangeDate
and c2.ChangeType IN (9,10) -- edit
where c0.ChangeType = 9 (lost) and c1.ChangeType = 10 (found)
and c2.ItemId IS NULL;
在上面的示例中,我假设ChangeDate是唯一的,我将&gt; =更改为&gt ;.如果ChangeDate不是唯一的,你将不得不提出一些其他的表达式来测试c0“在c0和c1之间”。
答案 1 :(得分:3)
这里的诀窍是规定'并且在丢失和找到的日期之间没有其他丢失或找到的日期',或者在SQL中:
SELECT c0.ItemId, c0.ChangeDate, c1.ChangeDate
FROM Changes AS c0
JOIN Changes AS c1 ON c0.ItemId = c1.ItemId AND c1.ChangeDate >= c0.ChangeDate
WHERE c0.ChangeType = 9 -- Lost
AND c1.ChangeType = 10 -- Found
AND NOT EXISTS(SELECT *
FROM Changes AS c2
WHERE c2.ItemId = c1.ItemID
AND c2.ChangeType IN (9, 10) -- Lost or Found
AND c2.ChangeDate BETWEEN c0.ChangeDate AND c1.ChangeDate
AND (c2.ChangeDate != c0.ChangeDate AND c2.ChangeDate != c1.ChangeDate)
);
因为这是一个相关的子查询,它往往会减慢查询速度,但它应该生成正确的行。
通过规定c2中行的ChangeDate应该与丢失的日期或找到的日期不同,有一个关于我消除c0和c1行的方式有一个重要的警告。但是,主查询似乎允许在丢失项目的同一天找到该项目。可能还有一些其他列 - 例如ChangeId列 - 在查询中未提及,可以替代使用:
AND c2.ChangeID NOT IN (c0.ChangeID, c1.ChangeID)
你需要考虑一件物品在2011-06-07丢失后会发生什么,并在2011-06-14再次丢失,并且只能在2011-06-21找到。如果它也在2011-06-28发现呢?这些问题应该通过数据输入处理来防止,因此上面的查询假定不存在这样的问题。