簡體   English   中英

嵌套選擇,聯合,左聯接

[英]NESTED SELECT, UNION, LEFT JOIN

我有一個涉及三個表的查詢:

  1. Employee
  2. Attendance
  3. Category

哪里

  • Employee PK為Id ;
  • Category的PK為Staff_id ;
  • 的PK Attendanceattendance_Id ;
  • Employee具有引用Category.Staff_id的外鍵Staff
  • Attendance具有引用Employee.Id的外鍵Id

我需要修改查詢以提供從第四個表Position提取的其他列position ,並按Position.positionEmployee.Staff將結果分組。 我無法修改任何表格的結構或內容。

結果行應如下所示,其中“驅動程序”對應於Staff = 2

職位| TotalEmp | 總出勤| TimeIn | 暫停

 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謂詞,這些謂詞與基表上的外鍵約束完全無關。 它使用不透明的表別名代替有意義的表別名。

原始查詢還具有一些非常可疑的結構:

  • 子查詢CD各自從表Employee中進行選擇,但所選列實際上都不來自該表。 所有這些都是不相關的聚合(子)查詢的結果,因此子查詢CD將分別提供與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的全部意義)。 還要注意,如果12Employee.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM