mysql - sql楼的进出记录
问题描述
从下表:
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”离开一次。汤姆是完美的!像汤姆一样!:)
Rob 也很完美,但他很嗜睡,所以他上下午班。:P
Anna 来和 Tom 一起工作。汤姆一直为她打开“门”,因此没有关于她进入的记录。此外,她不断回来,因为她忘记了一些东西。
约翰很狡猾!他来晚了,所以他应该解决它,但是当别人离开时,他总是和别人一起溜进“门”。
最后结婚。她在上夜班,因此需要在一张桌子上看到她一天分成两份记录。
是否有可能通过一个 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:对不起我的英语
解决方案
这是一个艰难的问题,但我想出了一个查询来做到这一点。它使用一些连接、子查询和联合,但会产生您想要的输出。我从@RajatJaiswals fiddle 开始,但创建了一个全新的查询。
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:00IFNULL(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
子句中,我使用 aJOIN
将进入建筑物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 Fiddle 中尝试:http ://sqlfiddle.com/#!9/e618bb/7/0
推荐阅读
- java - 如何将 chrome 窗口附加到桌面应用程序 Selenium?
- java - 有谁知道密码是如何在 xshell 会话中加密的
- laravel - 如何在 Laravel 7 Blade 中通过搜索使用 API?
- bash - 使用 netcat 创建多个连接,并通过每个 netcat 连接发送连续流量
- arduino - 为什么 ESP8266 模块可以连接到我的 iPhone 热点,但无法连接到我的家庭 wifi?
- python - 在 Python 中使用相同的函数以不同的方式打印堆栈或行
- machine-learning - 梯度下降的线性回归,八度
- reactjs - 在 React 中使用 react-hook-form 和子组件中的 Typescript
- flutter - 我们如何从 Windows 上在应用商店发布 Flutter 应用
- ios - 使用 ANY 和两个条件创建 NSPredicate