J'essaie d'obtenir des rapports pour les enregistrements de temps des employés.
Nous avons deux tableaux spécifiquement pour cette question. Les employés sont répertoriés dans le tableau Members
et chaque jour, ils saisissent les entrées d'heure du travail qu'ils ont effectué et sont stockés dans le Time_Entry
table.
Exemple de configuration avec SQL Fiddle: http://sqlfiddle.com/#!3/e3806/7
Le résultat final que je veux est un tableau qui montre [~ # ~] tous [~ # ~] le Members
dans une liste de colonnes, puis affichera leur somme d'heures pour la date interrogée dans les autres colonnes.
Le problème semble être que s'il n'y a pas de ligne dans le Time_Entry
table pour un membre particulier, il y a maintenant une ligne pour ce membre. J'ai essayé plusieurs types de jointures différents (gauche, droite, intérieure, extérieure, pleine extérieure, etc.) mais aucun ne semble me donner ce que je veux, ce qui serait (basé sur le dernier exemple dans 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
Ce que j'obtiens actuellement lorsque je demande une date précise de 11-1:
Member_ID | COUNTTime_Entry | TIMEENTRYDATE | SUMHOURS_ACTUAL | SUMHOURS_BILL
EGirsch | 1 | 11-10-2013 | 0.92 | 1
Ce qui est correct sur la base d'une ligne d'entrée unique datée du 11-10-2013 pour EGirsch, mais j'ai besoin de voir des zéros pour les autres membres afin d'obtenir des rapports et, éventuellement, un tableau de bord/rapport Web pour ces informations.
Ceci est ma première question, et pendant que je cherchais des requêtes Join, etc. Je ne sais vraiment pas comment cette fonction pourrait être appelée, donc j'espère que ce n'est pas un doublon et aidera les autres à essayer trouver une solution à des problèmes similaires.
Merci pour SQLfiddle et des exemples de données! J'aimerais que d'autres questions commencent de cette façon.
Si vous voulez que tous les membres aient une entrée pour cette date, vous voulez un LEFT OUTER JOIN
. Vous étiez très proche de cette version cependant une petite astuce avec les jointures externes est que si vous ajoutez un filtre à la table externe dans la clause WHERE
, vous transformez une jointure externe en une jointure interne join, car il exclura toutes les lignes qui sont NULL
de ce côté (car il ne sait pas si NULL
correspondrait ou non au filtre).
J'ai modifié la première requête pour obtenir une ligne pour chaque membre:
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
Je vais le laisser comme un exercice pour que le lecteur le prenne à partir de là et ajoute les autres colonnes, le formatage, COALESCE
etc.
Quelques autres notes:
veuillez toujours utiliser le préfixe de schéma lors de la création et du référencement d'objets
veuillez toujours utiliser une longueur lors de la conversion en varchar, etc.
évitez les formats de date régionaux ambigus comme mm-dd-yyyy
pensez à utiliser des alias pour faciliter la lecture de vos requêtes. Par exemple. ce qui précède pourrait être réécrit comme:
SELECT m.Member_ID
,t.Date_Start
,t.Hours_Actual
,t.Hours_Bill
FROM dbo.Members AS m
LEFT OUTER JOIN dbo.Time_Entry AS t
ON m.Member_ID = t.Member_ID
AND t.Date_Start = '20131110';
... beaucoup plus ordonné, à mon humble avis, tant que vous utilisez des alias sensés.
Lorsque j'ai été confronté à ce type de problème dans le passé, j'ai créé une table "nombres" pour aider à gérer les lignes manquantes.
J'ai créé ma table de nombres spécifiquement pour traiter les dates comme suit:
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
Cela crée un tableau avec une seule ligne pour chaque date entre 1900-01-01 et 2099-12-31. J'utilise TOP(73049)
pour limiter la plage de dates générée dans mon exemple à ces dates - si vous travaillez avec une plage de dates différente, vous pouvez ajuster ce nombre.
Ensuite, j'ajoute la table dDates
à ma requête afin qu'une ligne soit renvoyée pour chaque date dans la plage souhaitée pour chaque member_id
. Le résultat est ensuite joint à la table Time_Entry
En tant que tel:
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
Cela vous permet de spécifier une plage de dates pour le rapport.
Vous pouvez affiner davantage les résultats en ajoutant COALESCE(...)
et SUM(...)
selon:
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
Il en résulte la sortie suivante pour vos exemples de données: