STH*_*STH 5 sql join subquery left-join
我的雇主有一个批处理计算集群,用于处理用户提交的作业.每个批处理作业包括三个步骤:
批处理作业管理软件在每个步骤发生时记录,日志文件由一个元组组成,该元组具有提交作业的员工的ID代码,发生的步骤以及发生时间的时间戳.在CSV中,它看起来像:
ID step timestamp
-- ------ ---------
A start 3533
B start 3538
B finish 3549
C start 3551
A finish 3557
B report 3559
C finish 3602
A report 3603
B start 3611
C report 3623
B finish 3643
B report 3657
Run Code Online (Sandbox Code Playgroud)
等等.
数据集的另一个特点是员工之间存在共识,但员工之间并不相同; 也就是说,每个员工必须等到他们当前的工作在下一个工作开始前报告.因此,当我按日期排序并将结果限制为单个员工时,记录总是以"开始","完成","报告"的顺序出现.
我想创建一个数据透视表,将每个作业分组到一行.所以上面的数据变成:
employee-ID started finished reported
----------- ------- -------- --------
A 3533 3557 3603
B 3538 3549 3559
B 3611 3643 3657
C 3551 3602 3623
Run Code Online (Sandbox Code Playgroud)
那么,对于SQL:
SELECT
log.ID AS employee-ID,
start.timestamp AS started,
finish.timestamp AS finished,
report.timestamp AS reported
FROM
log
LEFT OUTER JOIN log start ON
log.ID = start.ID
AND start.step = 'start'
LEFT OUTER JOIN log finish ON
log.ID = finish.ID
AND finish.step = 'finish'
AND start.timestamp < finish.timestamp
LEFT OUTER JOIN log report ON
log.ID = report.ID
AND report.step = 'report'
AND finish.timestamp < report.timestamp
ORDER BY employee-ID,started,finished,reported;
Run Code Online (Sandbox Code Playgroud)
我确实需要LEFT OUTER JOIN,因为我还需要识别已启动但尚未完成或报告的作业.
这非常有效.它确实给了我需要的行.但它给了我很多伪行,因为JOIN的匹配finish和report条目,除了目前的工作,同样的员工未来的工作.所以报告看起来像:
employee-ID started finished reported
----------- ------- -------- --------
A 3533 3557 3603
B 3538 3549 3559
B 3538 3549 3657 <-- spurious
B 3538 3643 3657 <-- spurious
B 3611 3643 3657
C 3551 3602 3623
Run Code Online (Sandbox Code Playgroud)
很容易识别虚假行:每个作业只启动一次,因此在给定排序的情况下,正确的行是具有唯一"已启动"值的第一行.我现在正在应用程序级别处理虚假行问题,只是跳过虚假的行,但这看起来似乎不太优雅.而且成本很高:其中一些员工已经提交了数十个工作,因此目前,我的查询结果大约是15%的合法条目和85%的虚假条目.这是浪费在虚假条目上的大量浪费时间.如果每个作业都有一个唯一的ID会很好,但我没有那些数据.
我需要以某种方式限制JOIN,以便它为每个"已启动"条目仅选择一个"已完成"和"已报告"条目:具有最小时间戳大于前一步骤的时间戳的单个条目.我尝试通过使用子查询作为我正在加入的表来执行此操作,但是如果没有相关的子查询,我无法弄清楚如何执行此操作.我也尝试过使用"GROUP BY employee-ID,启动",但这并不一定会选择"正确"的行."GROUP BY"报告的大多数行都是错误的.
那么,SQL大师,是否可以只报告我需要的行?如果是这样,怎么样?我现在正在使用sqlite3,但如果需要可以将数据库传输到MySQL.
问题在于你如何加入finish并report
你不想要start.timestamp < finish.timestamp你真正想要的 start.timestamp < MIN(finish.timestamp)
当然,这是行不通的,因此您必须在加入后执行此操作。
例如
SELECT
log.ID AS employee_ID,
start.timestamp AS started,
MIN(finish.timestamp) AS finished,
MIN(report.timestamp) AS reported
FROM
log
LEFT OUTER JOIN log start ON
log.ID = start.ID
AND start.step = 'start'
LEFT OUTER JOIN log finish ON
log.ID = finish.ID
AND finish.step = 'finish'
AND start.timestamp < finish.timestamp
LEFT OUTER JOIN log report ON
log.ID = report.ID
AND report.step = 'report'
AND finish.timestamp < report.timestamp
GROUP BY log.ID,
start.timestamp
ORDER BY
employee_ID,started,finished,reported
Run Code Online (Sandbox Code Playgroud)
另外,您也可以将开始转换为内部连接,因为没有开始就结束没有多大意义