Truy vấn nối SQL để hiển thị các hàng với các hàng không tồn tại trong một bảng


12

Tôi đang cố gắng để có được một số báo cáo được thực hiện cho hồ sơ thời gian của nhân viên.

Chúng tôi có hai bảng đặc biệt cho câu hỏi này. Nhân viên được liệt kê trong Membersbảng và mỗi ngày họ nhập các mục thời gian làm việc họ đã thực hiện và được lưu trữ trong Time_Entrybảng.

Thiết lập ví dụ với SQL Fiddle: http://sqlfiddle.com/#!3/e3806/7

Kết quả cuối cùng tôi sẽ chọn là một bảng hiển thị TẤT CẢ các Membersdanh sách trong cột và sau đó sẽ hiển thị tổng số giờ của chúng cho ngày được truy vấn trong các cột khác.

Vấn đề dường như là nếu không có hàng trong Time_Entrybảng cho một thành viên cụ thể, thì bây giờ có hàng cho thành viên đó. Tôi đã thử một số loại tham gia khác nhau (Trái, Phải, Bên trong, Bên ngoài, Toàn bộ bên ngoài, v.v.) nhưng dường như không có loại nào mang lại cho tôi thứ tôi muốn, dựa trên ví dụ cuối cùng trong SQL Fiddle):

/*** Desired End Result ***/

Member_ID   | COUNTTime_Entry | TIMEENTRYDATE | SUMHOURS_ACTUAL | SUMHOURS_BILL
ADavis      | 0               | 11-10-2013    | 0               | 0
BTronton    | 0               | 11-10-2013    | 0               | 0
CJones      | 0               | 11-10-2013    | 0               | 0
DSmith      | 0               | 11-10-2013    | 0               | 0
EGirsch     | 1               | 11-10-2013    | 0.92            | 1
FRowden     | 0               | 11-10-2013    | 0               | 0

Những gì tôi hiện đang nhận được khi tôi truy vấn cho ngày cụ thể 11-1:

Member_ID   | COUNTTime_Entry | TIMEENTRYDATE | SUMHOURS_ACTUAL | SUMHOURS_BILL
EGirsch     | 1               | 11-10-2013    | 0.92            | 1

Điều này đúng dựa trên một hàng Thời gian nhập ngày 11-10-2013 cho EGirsch, nhưng tôi cần xem số không cho các thành viên khác để nhận báo cáo và cuối cùng là bảng điều khiển / báo cáo web cho thông tin này.

Đây là câu hỏi đầu tiên của tôi và trong khi tôi tìm kiếm các truy vấn Tham gia, v.v ... Tôi thực sự không chắc chức năng này có thể được gọi là gì, vì vậy tôi hy vọng rằng đây không phải là một bản sao và sẽ giúp những người khác cố gắng tìm giải pháp để vấn đề tương tự.

Câu trả lời:


11

Cảm ơn bạn về SQLfiddle và dữ liệu mẫu! Tôi muốn nhiều câu hỏi bắt đầu theo cách này.

Nếu bạn muốn tất cả các thành viên bất kể họ có mục nào cho ngày đó hay không, bạn muốn a LEFT OUTER JOIN. Bạn đã rất thân với phiên bản này, tuy nhiên một mẹo nhỏ với các phép nối ngoài là nếu bạn thêm bộ lọc vào bảng bên ngoài trong WHEREmệnh đề, bạn chuyển một phép nối ngoài sang một phép nối bên trong, bởi vì nó sẽ loại trừ bất kỳ hàng nào NULLở bên đó (vì không biết NULLcó phù hợp với bộ lọc hay không).

Tôi đã sửa đổi truy vấn đầu tiên để nhận một hàng cho mọi thành viên:

SELECT Members.Member_ID
      ,Time_Entry.Date_Start
      ,Time_Entry.Hours_Actual
      ,Time_Entry.Hours_Bill
FROM dbo.Members
  LEFT OUTER JOIN dbo.Time_Entry
--^^^^ changed from FULL to LEFT
  ON Members.Member_ID = Time_Entry.Member_ID
  AND Time_Entry.Date_Start = '20131110';
--^^^ changed from WHERE to AND

Tôi sẽ để nó như một bài tập để người đọc lấy nó từ đó và thêm các cột khác, định dạng, COALESCEv.v.

Một số lưu ý khác:


Aaron, cảm ơn rất nhiều vì đã phản hồi. SQL newbie ở đây, và không có ý tưởng về sự khác biệt giữa WHEREAND. Tôi đã sử dụng các bí danh ban đầu, nhưng sqlfiddle dường như không thích nó vì vậy tôi chỉ đi đến định dạng đầy đủ. Cảm ơn các mẹo SQL khác là tốt. Bạn có đề nghị ISNULLhoặc COALESCElàm cho dữ liệu 0 thay vì NULL? Cảm ơn một lần nữa!
chia tay

1
@farewelldave Tôi thích COALESCE hơn vì nó là tiêu chuẩn và không đi chệch khỏi chức năng của nó trong các ngôn ngữ khác (ví dụ như cách ISNULL hoạt động trong SQL Server so với VB chẳng hạn). Trong hầu hết các trường hợp, sự khác biệt hiệu suất là không quan trọng, ngoại trừ một. Nhiều chi tiết hơn ở đây .
Aaron Bertrand

4

Khi tôi phải đối mặt với loại vấn đề này trong quá khứ, tôi đã tạo một bảng "số" để giúp xử lý các hàng bị thiếu.

Tôi đã tạo bảng số của mình một cách cụ thể để xử lý ngày tháng như vậy:

CREATE TABLE Dates
(
    dDate DATETIME NOT NULL CONSTRAINT PK_Dates PRIMARY KEY CLUSTERED
);

INSERT INTO Dates (dDate)
SELECT TOP(73049) DATEADD(d, -1, ROW_NUMBER() OVER (ORDER BY o.object_id)) AS dDate
FROM master.sys.objects o, master.sys.objects o1, master.sys.objects o2

Điều này tạo ra một bảng với một hàng duy nhất cho mỗi và mỗi ngày trong khoảng thời gian từ 1900-01-01 đến 2099-12-31. Tôi sử dụng TOP(73049)để giới hạn phạm vi ngày được tạo trong ví dụ của mình thành ngày tháng - nếu bạn làm việc với phạm vi ngày khác, bạn có thể điều chỉnh số đó.

Tiếp theo, tôi thêm dDatesbảng vào truy vấn của mình để một hàng được trả về cho mỗi ngày trong phạm vi mong muốn cho mỗi member_id. Kết quả sau đó được nối vào Time_Entrybảng như sau:

SELECT MD.Member_ID,
    MD.dDate,
    T.Date_Start,
    T.Hours_Actual,
    T.Hours_Bill
FROM 
    (
        SELECT M.Member_ID, D.dDate
        FROM dbo.Dates D, dbo.Members M
        WHERE D.dDate >= '20131110' AND D.dDate < '20131112'
    ) AS MD
    LEFT JOIN dbo.Time_Entry T ON MD.Member_ID = T.Member_ID AND MD.dDate = T.Date_Start
ORDER BY MD.Member_ID, MD.dDate

Điều này cho phép bạn chỉ định phạm vi ngày cho báo cáo.

Bạn có thể tinh chỉnh thêm kết quả bằng cách thêm COALESCE(...)SUM(...)theo:

SELECT MD.Member_ID,
    MD.dDate,
    T.Date_Start,
    SUM(COALESCE(T.Hours_Actual, 0)) AS TotalHoursActual,
    SUM(COALESCE(T.Hours_Bill, 0)) AS TotalHoursBill
FROM 
    (
        SELECT M.Member_ID, D.dDate
        FROM dbo.Dates D, dbo.Members M
        WHERE D.dDate >= '20131110' AND D.dDate < '20131112'
    ) AS MD
    LEFT JOIN dbo.Time_Entry T ON MD.Member_ID = T.Member_ID AND MD.dDate = T.Date_Start
GROUP BY MD.Member_ID, MD.dDate, T.Date_Start
ORDER BY MD.Member_ID, MD.dDate

Điều này dẫn đến kết quả đầu ra sau cho dữ liệu mẫu của bạn:

nhập mô tả hình ảnh ở đây


Cảm ơn, Max. Bạn có thể tìm thấy rất nhiều thông tin về kỹ thuật này bằng cách tìm kiếm "bảng kiểm đếm" thay vì "bảng số". Chúng rất tốt để cải thiện hiệu suất bằng cách chuyển đổi các hoạt động bằng cách sử dụng các con trỏ / vòng lặp thành các hoạt động bằng cách sử dụng các bộ. Cơ sở dữ liệu quan hệ thích bộ.
Suncat2000

1
@ Suncat2000 - đã đồng ý, mặc dù tôi thích tên "bảng số" hơn vì hàm ý bổ sung và theo kinh nghiệm của tôi, mẫu này hiếm khi được sử dụng cho các hoạt động toán học. Chúng tuyệt vời cho nhiều thứ, nhưng chắc chắn một trong những cải tiến hiệu suất lớn nhất bạn có thể nhận được là từ cách tiếp cận RBAR, đến cách tiếp cận dựa trên tập hợp, bằng cách sử dụng bảng số.
Max Vernon
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.