[英]NESTED SELECT, UNION, LEFT JOIN
我有一个涉及三个表的查询:
Employee
Attendance
Category
哪里
Employee
PK为Id
; Category
的PK为Staff_id
; Attendance
是attendance_Id
; Employee
具有引用Category.Staff_id
的外键Staff
; Attendance
具有引用Employee.Id
的外键Id
我需要修改查询以提供从第四个表Position
提取的其他列position
,并按Position.position
和Employee.Staff
将结果分组。 我无法修改任何表格的结构或内容。
结果行应如下所示,其中“驱动程序”对应于Staff = 2
:
Driver 5 5 8.00am 6.00pm
这是我当前的查询:
SELECT D.TotalEmp, D.TotalAttendance, D.Timein, D.TimeOut
FROM (
SELECT B.TotalEmp, B.TimeIn, B.TimeOut FROM (
SELECT
(SELECT COUNT (distinct Id) FROM Employee WHERE Staff = 2) AS TotalEmp,
(
SELECT COUNT(id)
FROM Attendance Q
WHERE
id IN (SELECT (Id) FROM Employee WHERE Staff = 2)
AND CONVERT(datetime, CONVERT(nvarchar(10), Q.timeInDate, 103), 103) = '20/11/2014'
) AS TotalAttendance,
(
SELECT MIN(CONVERT(VARCHAR(8),I.timeInDate,108))
FROM Attendance I
WHERE
CONVERT(datetime, CONVERT(nvarchar(10), I.timeInDate, 103), 103) = '20/11/2014'
AND I.id IN (SELECT (Id) FROM Employee WHERE Staff = 2)
) Timein,
(
SELECT
MAX(CONVERT(VARCHAR(8),O.timeOutDate,108))
FROM Attendance O
WHERE
CONVERT(datetime, CONVERT(nvarchar(10), O.timeOutDate, 103), 103) = '20/11/2014'
AND O.id IN (SELECT (Id) FROM Employee WHERE Staff = 2)
) TimeOut
FROM Employee
WHERE Id IN (SELECT (id) FROM Attendance)
) B
UNION
SELECT C.TotalEmp, C.Time, C.TimeOut FROM (
SELECT
(SELECT COUNT (distinct Id) FROM Employee WHERE Staff = 1) AS TotalEmployee,
(
SELECT COUNT(id)
FROM Attendance R
WHERE
id IN (SELECT (Id) FROM Employee WHERE Staff = 1)
AND CONVERT(datetime, CONVERT(nvarchar(10), R.timeInDate, 103), 103) = '20/11/2014'
) AS TotalAttendance,
(
SELECT MIN(CONVERT(VARCHAR(8), T.timeInDate, 108))
FROM Attendance T
WHERE
CONVERT(datetime, CONVERT(nvarchar(10), T.timeInDate, 103), 103) = '20/11/2014'
AND T.id IN (SELECT (Id) FROM Employee WHERE Staff = 1)
) Timein,
(
SELECT MAX(CONVERT(VARCHAR(8),X.timeOutDate,108))
FROM Attendance X
WHERE
CONVERT(datetime, CONVERT(nvarchar(10), X.timeOutDate, 103), 103) = '20/11/2014'
AND X.id IN (SELECT (Id) FROM Employee WHERE Staff = 1)
) TimeOut
FROM Employee
WHERE Id IN (SELECT (id) FROM Attendance)
) C
) D
GROUP BY D.TotalEmp, D.TotalAttendance, D.Timein, D.TimeOut
如何修改查询以产生所需的结果?
希望您能原谅我说您的原始原始查询非常可怕。 它统一执行更适合连接的子查询,并且它具有多个子查询,这些子查询应作为公共表表达式甚至是顶级聚合被分解出来。 它还表达了一些WHERE
谓词,这些谓词与基表上的外键约束完全无关。 它使用不透明的表别名代替有意义的表别名。
原始查询还具有一些非常可疑的结构:
子查询C
和D
各自从表Employee
中进行选择,但所选列实际上都不来自该表。 所有这些都是不相关的聚合(子)查询的结果,因此子查询C
和D
将分别提供与Employee
行一样多的行,并且所有行都是相同的(每个子查询)。 然后,当UNION
运算符消除重复的行时,将再次除去所有那些不需要的重复。
您在最外面的查询上具有GROUP BY
子句,但是该查询的选择列表中没有聚合函数。 也许您想改为对这些列进行ORDER BY
,但如果不是,则GROUP BY
完全没有用。
您正在将日期转换为字符串以进行比较; 对于平等比较,这不一定是错误的,但是效率低下。 但是,大于和小于比较是错误的,因此与MIN()
和MAX()
一起使用也是错误的。 但是,在某些情况下,通过产生正确的结果来欺骗您会足够好。
您执行具有相同结构的两个子查询的UNION
,仅在某些查询谓词上有所不同。 这就要求将其合并为一个查询。
从简化原始查询开始肯定会有所帮助。 看起来这将产生相同的数据,只是添加了Staff
列,并且顺序可能不同:
SELECT
emp.Staff,
COUNT(DISTINCT emp.id) AS TotalEmp,
COUNT(DISTINCT att.id) AS TotalAttendance,
MIN(att.timeInDate) AS TimeIn,
MAX(att.timeOutDate) AS TimeOut,
FROM
Employee emp
LEFT JOIN Attendance att ON att.Id = emp.Id
WHERE
CAST(att.timeInDate AS DATE) = CONVERT(DATE, '20/11/2014', 103)
AND (emp.Staff = 1 OR emp.Staff = 2)
GROUP BY emp.Staff
注意它确实按Staff
分组; 这消除了对UNION
的需求,同时仍保留了每员工的汇总值(实际上,这就是GROUP BY
的全部意义)。 还要注意,如果1
和2
是Employee.Staff
的唯一可能值,或者如果您也可以获取其他值的结果,那么可以通过删除WHERE
条件将结果限制为仅来进一步简化这些价值观。
另请注意,您的Datetime
值将转换为Date
以去除时间部分; 这比将它们格式化为字符串要有效得多。 您的文字日期字符串将转换为Date
以进行比较(使用格式103)。
这是一个更好的起点,因为数据的结构和分组的性质很清楚。 而且非常简单! 现在,如果您想以不同方式拆分组,则很容易做到。
特别是,这样的事情应该可以满足您的要求:
SELECT
pos.position AS position,
COUNT(DISTINCT emp.id) AS TotalEmp,
COUNT(DISTINCT att.id) AS TotalAttendance,
MIN(att.timeInDate) AS TimeIn,
MAX(att.timeOutDate) AS TimeOut,
FROM
Employee emp
JOIN Position pos ON emp.position_id = pos.positionId
LEFT JOIN Attendance att ON att.Id = emp.Id
WHERE
CAST(att.timeInDate AS DATE) = CONVERT(DATE, '20/11/2014', 103)
GROUP BY pos.position
这依赖于以下事实:每个职位恰好与一个Staff
值相关联,因此按Staff
分组也不会获得任何收益。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.