SQL-запит приєднання для показу рядків з неіснуючими рядками в одній таблиці


12

Я намагаюся зробити деяку звітність для обліку часу працівників.

У нас є дві таблиці спеціально для цього питання. Співробітники перераховані в Membersтаблиці, і кожен день вони вносять часові записи роботи, яку вони виконували, і зберігаються в Time_Entryтаблиці.

Приклад налаштування за допомогою SQL Fiddle: http://sqlfiddle.com/#!3/e3806/7

В кінцевому підсумку я буду за це таблиця , яка показує все , що Membersв списку стовпців , а потім буде показувати свій годинник сум на дату запитані в інших стовпцях.

Проблема, здається, полягає в тому, що якщо у Time_Entryтаблиці немає рядка для конкретного члена, тепер для нього є рядок. Я спробував декілька різних типів приєднання (лівий, правий, внутрішній, зовнішній, повний зовнішній тощо), але жоден, здається, не дає мені того, що я хочу, що було б (на основі останнього прикладу в 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

Що я зараз отримую, коли запитую конкретну дату 11-1:

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

Це правильно, виходячи з одного рядка Time Entry, який датований 11-10-2013 для EGirsch, але мені потрібно побачити нулі для інших членів, щоб отримати звіти та, зрештою, веб-панель / звіт для цієї інформації.

Це моє перше питання, і, поки я шукав запити на приєднання, і т. Д. Я, чесно кажучи, не впевнений, як ця функція може бути названа, тому я сподіваюся, що це не дублікат і допоможе іншим намагатися знайти рішення для подібні проблеми.

Відповіді:


11

Дякуємо за SQLfiddle та вибіркові дані! Бажаю, щоб більше питань почалося саме так.

Якщо ви хочете, щоб усі учасники незалежно від того, чи мають вони запис на цю дату, вам потрібно LEFT OUTER JOIN. Ви були дуже близькі з цією версією, проте невелика хитрість із зовнішніми приєднаннями полягає в тому, що якщо ви додаєте фільтр до зовнішньої таблиці в WHEREпункті, ви перетворите зовнішнє з'єднання на внутрішнє з'єднання, оскільки воно буде виключати будь-які рядки, що знаходяться NULLна цій стороні (тому що не знає NULL, відповідатиме він чи ні).

Я змінив перший запит, щоб отримати рядок для кожного учасника:

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

Я залишу це як вправу для читача, щоб взяти його звідти і додати інші стовпці, форматування COALESCEтощо.

Деякі інші примітки:


Аарон, дуже дякую за відгуки. Новачок SQL тут, і поняття не мав, різниця між WHEREі AND. Я використовував псевдоніми спочатку, але sqlfiddle не сподобалось, тому я просто перейшов у повний формат. Дякуємо також за інші поради SQL. Ви б рекомендувати ISNULLабо COALESCEзробити дані 0 замість NULL? Знову дякую!
прощання

1
@farewelldave Я віддаю перевагу COALESCE, оскільки він стандартний і не відхиляється від його функціональності на інших мовах (порівняйте, наприклад, як ISNULL працює в SQL Server проти VB, наприклад). Майже у всіх випадках різниця в продуктивності є несуттєвою, крім однієї. Докладніше тут .
Аарон Бертран

4

Коли в минулому я стикався з подібним типом проблем, я створив таблицю "цифр", щоб допомогти впоратися з відсутніми рядками.

Я створив свою таблицю номерів спеціально, щоб мати справу з датами так:

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

Це створює таблицю з одним рядком для кожної дати між 1900-01-01 та 2099-12-31. Я використовую TOP(73049)для обмеження діапазону дат, створеного в моєму прикладі, до цих дат - якщо ви працюєте з іншим діапазоном дат, ви можете скорегувати це число.

Далі я додаю dDatesтаблицю до мого запиту, щоб рядок повертався для кожної дати в потрібному діапазоні для кожного member_id. Потім результат приєднується до Time_Entryтаблиці як такого:

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

Це дозволяє вказати діапазон дат для звіту.

Ви можете додатково уточнити результати, додавши COALESCE(...)та SUM(...)відповідно до:

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

Це призводить до отримання наступних результатів для ваших вибіркових даних:

введіть тут опис зображення


Спасибі, Макс. Ви можете знайти велику кількість інформації про цю техніку, скориставшись пошуком "таблиця підрахунку" замість "таблиці цифр". Вони чудово підходять для підвищення продуктивності, перетворюючи операції за допомогою курсорів / циклів в операції з використанням наборів. Реляційні бази даних віддають перевагу наборам.
Suncat2000

1
@ Suncat2000 - погодився, хоча я віддаю перевагу назві "таблиця чисел", оскільки підрахунок передбачає додавання, і на мій досвід, ця модель рідко використовується для математичних операцій. Вони чудово підходять для багатьох речей, але, безумовно, одне з найбільших покращень продуктивності, яке ви можете отримати, - це перехід від RBAR-підходу до набору на основі набору, використовуючи таблицю цифр.
Макс Вернон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.