[英]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.