从下表中:
timestamp inout Name
2018-04-01 14:00 0 Tom
2018-04-02 06:00 1 Tom
2018-04-02 14:00 0 Tom
2018-04-03 06:00 1 Tom
2018-04-01 22:00 0 Rob
2018-04-02 14:00 1 Rob
2018-04-02 22:00 0 Rob
2018-04-03 13:00 1 Rob
2018-04-01 12:55 0 John
2018-04-02 06:05 1 John
2018-04-03 06:10 1 John
2018-04-01 14:05 0 Anna
2018-04-02 14:10 0 Anna
2018-04-02 14:15 1 Anna
2018-04-02 14:20 0 Anna
2018-04-03 14:05 0 Anna
2018-04-01 22:00 1 Mary
2018-04-02 06:00 0 Mary
2018-04-02 22:00 1 Mary
2018-04-03 06:00 0 Mary
其中1 = in 0 = out
我需要收集2018-04-02的“进入和退出记录”的数据,并将其显示在这样的表中:
d1-in-timestamp d0-out-timestamp Name
2018-04-02 07:00 2018-04-02 15:00 Tom
2018-04-02 14:00 2018-04-02 22:00 Rob
2018-04-02 06:05 - John
- 2018-04-02 14:10 Anna
2018-04-02 14:15 2018-04-02 14:20 Anna
2018-04-02 00:00 2018-04-02 06:00 Mary
2018-04-02 22:00 2018-04-02 00:00 Mary
在一个完美的世界中
汤姆通过“DOOR”进入建筑物,然后经过“DOOR”一次。汤姆很完美!就像汤姆! :)
罗布也很完美,但他很瞌睡,所以他来到了下午班。 :P
安娜来和汤姆一起工作。汤姆为她保持“DOOR”开放,所以没有关于她进入的记录。此外,她一直回来,因为她忘记了什么
约翰是道奇!他上班迟到了,所以他应该解决这个问题,但是当别人离开时,他总是和别人一起通过“DOOR”滑倒。
最后结婚。她是夜班,所以她需要在一张桌子上看到一天分成两条记录
是否可以通过一个SQL查询在一个表中获得此类结果?
到目前为止,我管理这样的SQL查询:
select timestamp as d1, (select timestamp from DOOR where timestamp>m1.timestamp and inout=0 and name=m1.name) as d0, name from DOOR as m1 where substring(timestamp,1,10)='2018-04-02' and inout=1 order by name, timestamp
查询适用于来自“完美世界”(Tom& Rob)的人,而对于约翰则更多/更少 不幸的查询对Anna和Marry不起作用。
PS:抱歉我的英文
答案 0 :(得分:1)
这是一个艰难的,但我想出了一个查询来做到这一点。它使用一些连接,子查询和联合,但会产生您想要的输出。我从@RajatJaiswals小提琴开始,但创建了一个全新的查询。
SELECT * FROM (
SELECT
IF(inA.timestamp < '2018-04-02', '2018-04-02 00:00:00', inA.timestamp) AS `d1-in-timestamp`,
IFNULL(IF(outA.timestamp > '2018-04-02 23:59:59', CAST(DATE_ADD('2018-04-02', INTERVAL 1 DAY) AS DATETIME), outA.timestamp), '-') AS `d0-out-timestamp`,
inA.name AS `Name`
FROM
attendance AS inA
LEFT JOIN attendance AS outA ON (
inA.name = outA.name
AND outA.inout = 0
AND inA.timestamp < outA.timestamp
AND NOT EXISTS(
SELECT betweenA.name
FROM attendance AS betweenA
WHERE
betweenA.name = inA.name
AND betweenA.timestamp > inA.timestamp
AND betweenA.timestamp < outA.timestamp
)
)
WHERE
inA.inout = 1
AND (
CAST(inA.timestamp AS DATE) = '2018-04-02'
OR CAST(outA.timestamp AS DATE) = '2018-04-02'
)
UNION
SELECT
'-' AS `d1-in-timestamp`,
IF(outA.timestamp > '2018-04-02 23:59:59', CAST(DATE_ADD('2018-04-02', INTERVAL 1 DAY) AS DATETIME), outA.timestamp) AS `d0-out-timestamp`,
outA.name AS `Name`
FROM
attendance AS outA
LEFT JOIN attendance AS inA ON (
inA.name = outA.name
AND inA.inout = 1
AND inA.timestamp < outA.timestamp
AND NOT EXISTS(
SELECT betweenA.name
FROM attendance AS betweenA
WHERE
betweenA.name = inA.name
AND betweenA.timestamp > inA.timestamp
AND betweenA.timestamp < outA.timestamp
)
)
WHERE
outA.inout = 0
AND CAST(outA.timestamp AS DATE) = '2018-04-02'
AND inA.name IS NULL
) AS a
ORDER BY `Name`, `d1-in-timestamp`
这是一个复杂的查询,起初可能看起来令人生畏,但我尝试将其分解成一小部分来解释它的作用:
外部SELECT
只是为了对整个结果进行排序。由于UNION
语句,这是必需的。
SELECT
子句只处理一些输出转换
IF(inA.timestamp < '2018-04-02', '2018-04-02 00:00:00', inA.timestamp)
仅用于格式化,并替换当天00:00之前的时间戳,IFNULL(IF(outA.timestamp > '2018-04-02 23:59:59', CAST(DATE_ADD('2018-04-02', INTERVAL 1 DAY) AS DATETIME), outA.timestamp), '-')
再次进行格式化,但会做两件事:如果此人没有离开建筑物,则将{null替换为-
,并替换当天的00:00之后的时间戳。 在FROM
子句中,我使用JOIN
将进入建筑物(inA
)的记录与离开建筑物的记录(outA
)连接在一起。有趣的部分是ON
子句:
name
列仅加入同一个人的记录outA
表应该只关注离开的人(inout = 0
)inA.timestamp < outA.timestamp
如果此人在离开这两个条目之前没有进入,则不应该联合起来加入的两个记录之间不应有任何活动记录。这由NOT EXISTS
子查询处理。它搜索
betweenA.name = inA.name
)inA
有问题的记录outA
记录之前如果存在任何此类记录,则NOT EXISTS
子句的计算结果为false,并且记录未加入。这样,只有后续的进出条目才能连接在一起。
WHERE
子句很简单:
inA
CAST(inA.timestamp AS DATE)
将时间戳转换为日期,从而删除时间部分并简化比较)选择记录进入建筑物的人的所有记录。我们现在仍然怀念没有记录进入大楼的安娜的情况。这就是UNION
到位的地方,并将此信息添加到结果中。
SELECT
再次只是输出逻辑:
ON
子句执行以下操作:
name
仅加入一个人的记录outA.inout = 1
因为联接表应该只使用输入inA.timestamp < outA.timestamp
)WHERE
子句再次执行一些重要限制:
outA.inout = 0
因为我们需要将表限制在离开建筑物的记录中CAST(outA.timestamp AS DATE) = '2018-04-02'
这次只检查outA
的日期,因为没有进入记录。inA.name IS NULL
最后一件事是ORDER BY
条款应该是自我解释的。
| d1-in-timestamp | d0-out-timestamp | Name |
|---------------------|---------------------|------|
| - | 2018-04-02 14:10:00 | Anna |
| 2018-04-02 14:15:00 | 2018-04-02 14:20:00 | Anna |
| 2018-04-02 06:05:00 | - | John |
| 2018-04-02 00:00:00 | 2018-04-02 06:00:00 | Mary |
| 2018-04-02 22:00:00 | 2018-04-03 00:00:00 | Mary |
| 2018-04-02 14:00:00 | 2018-04-02 22:00:00 | Rob |
| 2018-04-02 06:00:00 | 2018-04-02 14:00:00 | Tom |
您可以在以下SQL小提琴中尝试:http://sqlfiddle.com/#!9/e618bb/7/0
答案 1 :(得分:0)
SELECT t.Name,t1.TimeStamp asintime,t.timestamp as outtime
FROM tmpAttendance t
LEFT OUTER JOIN tmpAttendance t1 ON t.Name = t1.Name
AND t1.inout = 1
AND CAST(t.timestamp AS DATE) = CAST(t1.timestamp AS DATE)
WHERE t.inout = 0
ORDER BY t.Name ,t1.TimeStamp
答案 2 :(得分:0)
只需添加一个简单的内容:为Mary添加一行最长时间(2018-04-02 23:59)
http://sqlfiddle.com/#!9/bb41b1/6
然后你可以使用下面的逻辑:
select a.name,
case when a.name != 'Mary' and (b.out_time is null or a.in_time <= b.out_time) then a.in_time
when a.name != 'Mary' and b.out_time is not null and a.in_time > b.out_time then null
when a.name = 'Mary' and a.in_time > b.out_time then '2018-04-02 00:00:00'
else a.in_time
end as in_time,
b.out_time
from
(select name,time1 as in_time from have1
where inout1 = 1 and (substring(time1,1,10)='2018-04-02')) a
left join
(select name,time1 as out_time from have1
where inout1 = 0 and (substring(time1,1,10)='2018-04-02')) b
on a.name = b.name
<强>输出:强>
name in_time out_time
Tom 2018-04-02 06:00:00 2018-04-02 14:00:00
Rob 2018-04-02 14:00:00 2018-04-02 22:00:00
John 2018-04-02 06:05:00 (null)
Anna (null) 2018-04-02 14:10:00
Anna 2018-04-02 14:15:00 2018-04-02 14:20:00
Mary 2018-04-02 00:00:00 2018-04-02 06:00:00
Mary 2018-04-02 22:00:00 2018-04-02 23:59:59
如果需要澄清,请告诉我。
答案 3 :(得分:0)
玛丽和其他夜班人员
首先让我们谈谈玛丽和其他夜班人员。当我们过滤所有邮票时,我们可以找到它们的邮票:
对于外面的印章,我们使用印章的第二天00:00。
SELECT d.`timestamp` `timestamp_in`,
timestampadd(second, -1 * second(d.`timestamp`), timestampadd(minute, -1 * minute(d.`timestamp`), timestampadd(hour, -1 * hour(d.`timestamp`), timestampadd(day, 1, (d.`timestamp`))))) `timestamp_out`,
d.`name`
FROM `door` d
WHERE d.`inout` = 1
AND NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND di.`timestamp` > d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
AND EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND dii.`timestamp` < di.`timestamp`
AND dii.`inout` = 1))
为了获得夜间移位器的标记,我们可以使用相同的逻辑,但切换所有内容:
>
在日期比较中变为<
,反之亦然。0
,则标记为1
,反之亦然。对于印章,我们使用印章日当天00:00。
如果我们UNION ALL
两个查询,我们都会获得夜间移位器的部分。
该团伙的其余成员也称为日间移民
现在可以轻松获得日间移位器的印章。我们只需要否定我们用来寻找夜班者的条件。获得印章:
SELECT d.`timestamp`,
d.`name`
FROM `door` d
WHERE d.`inout` = 1
AND (EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND di.`timestamp` > d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
OR NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND dii.`timestamp` < di.`timestamp`
AND dii.`inout` = 1)))
对于外面的邮票,我们可以再次切换,>
变为<
等,如上所述。
当然,在这里我们不能为邮票生成印章,反之亦然。我们必须FULL OUTER JOIN
邮票和日间变速器的邮票。但MySQL至少在较低版本中不支持此操作。因此,我们按UNION
两个LEFT JOIN
进行操作,将第二个LEFT JOIN
中的角色与第一个角色进行比较。
全部放在一起
现在我们可以简单地UNION ALL
夜间移民和日间移位器来获取所有日子的完整结果。我们可以SELECT FROM
,以获得特定日期的结果:
SELECT coalesce(x.`timestamp_in`, '-') `d1-in-timestamp`,
coalesce(x.`timestamp_out`, '-') `d0-out-timestamp`,
x.`name`
FROM (SELECT r.`timestamp` `timestamp_in`,
s.`timestamp` `timestamp_out`,
r.`name`
FROM (SELECT d.`timestamp`,
d.`name`
FROM `door` d
WHERE d.`inout` = 1
AND (EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND di.`timestamp` > d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
OR NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND dii.`timestamp` < di.`timestamp`
AND dii.`inout` = 1)))) r
LEFT JOIN (SELECT d.`timestamp`,
d.`name`
FROM `door` d
WHERE d.`inout` = 0
AND (EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND di.`timestamp` < d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
OR NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND dii.`timestamp` > di.`timestamp`
AND dii.`inout` = 0)))) s
ON s.`name` = r.`name`
AND date(s.`timestamp`) = date(r.`timestamp`)
AND s.`timestamp` > r.`timestamp`
UNION
SELECT u.`timestamp` `timestamp_in`,
t.`timestamp` `timestamp_out`,
t.`name`
FROM (SELECT d.`timestamp`,
d.`name`
FROM `door` d
WHERE d.`inout` = 0
AND (EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND di.`timestamp` < d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
OR NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND dii.`timestamp` > di.`timestamp`
AND dii.`inout` = 0)))) t
LEFT JOIN (SELECT d.`timestamp`,
d.`name`
FROM `door` d
WHERE d.`inout` = 1
AND (EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND di.`timestamp` > d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
OR NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND dii.`timestamp` < di.`timestamp`
AND dii.`inout` = 1)))) u
ON u.`name` = t.`name`
AND date(u.`timestamp`) = date(t.`timestamp`)
AND u.`timestamp` < t.`timestamp`
UNION
SELECT d.`timestamp` `timestamp_in`,
timestampadd(second, -1 * second(d.`timestamp`), timestampadd(minute, -1 * minute(d.`timestamp`), timestampadd(hour, -1 * hour(d.`timestamp`), timestampadd(day, 1, (d.`timestamp`))))) `timestamp_out`,
d.`name`
FROM `door` d
WHERE d.`inout` = 1
AND NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND di.`timestamp` > d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
AND EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND dii.`timestamp` < di.`timestamp`
AND dii.`inout` = 1))
UNION ALL
SELECT timestampadd(second, -1 * second(d.`timestamp`), timestampadd(minute, -1 * minute(d.`timestamp`), timestampadd(hour, -1 * hour(d.`timestamp`), d.`timestamp`))) `timestamp_in`,
d.`timestamp` `timestamp_out`,
d.`name`
FROM `door` d
WHERE d.`inout` = 0
AND NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND di.`timestamp` < d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
AND EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND dii.`timestamp` > di.`timestamp`
AND dii.`inout` = 0))) x
WHERE date(x.`timestamp_in`) = '2018-04-02'
OR date(x.`timestamp_out`) = '2018-04-02'
ORDER BY x.`name`,
x.`timestamp_in`;