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

12

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

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

Пример настройки с использованием SQL Fiddle: http://sqlfiddle.com/#!3/e3806/7

В конечном итоге я буду за это таблица , которая показывает все , что Membersв списке столбцов , а затем будет показывать свои часы сумм на дату запрошены в других столбцах.

Кажется, проблема в том, что если в Time_Entryтаблице нет строки для конкретного члена, теперь есть строка для этого члена. Я пробовал несколько разных типов соединения (Left, Right, Inner, Outer, Full Outer и т. Д.), Но ни один из них, по-видимому, не дает мне то, что я хочу, что будет (на основе последнего примера в 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

Это верно, основываясь на одной строке ввода времени, которая датирована 11-10-2013 для EGirsch, но мне нужно видеть нули для других участников, чтобы получать отчеты и, в конечном итоге, веб-панель / отчет для этой информации.

Это мой первый вопрос, и хотя я искал запросы на присоединение и т. Д. Я, честно говоря, не уверен, как эта функция может быть вызвана, поэтому я надеюсь, что это не дубликат и поможет другим тоже пытаться найти решение. похожие проблемы.

farewelldave
источник

Ответы:

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 к подходу на основе множеств с использованием таблицы чисел.
Макс Вернон,