私は従業員の時間記録についていくつかのレポートを作成しようとしています。
この質問に特化した2つの表があります。従業員はMembers
テーブルにリストされており、毎日、彼らが実行した作業の時間エントリを入力し、Time_Entry
テーブルに保存されます。
SQL Fiddleの設定例: http://sqlfiddle.com/#!3/e3806/7
私がしようとしている最終結果は、[〜#〜] all [〜#〜]を示すテーブルです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
これはEGirschの2013年11月10日付けの1つのタイムエントリ行に基づいて正しいですが、レポートを取得し、最終的にこの情報のWebダッシュボード/レポートを取得するには、他のメンバーのゼロを表示する必要があります。
これは私の最初の質問です。Joinクエリなどを検索しましたが、この関数が何と呼ばれるかは正直わかりません。これが重複しておらず、他の人の役に立つことを願っています同様の問題の解決策を見つけようとしています。
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
などを追加するための演習として残しておきます。
その他の注意事項:
mm-dd-yyyy
のようなあいまいな地域の日付形式は避けてください
クエリを読みやすくするために、エイリアスの使用を検討してください。例えば。上記は次のように書き直すことができます。
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';
...賢明なエイリアスを使用している限り、私見はかなり整然としています。
過去にこの種の問題に直面したことがある場合は、不足している行の処理に役立つ "numbers" テーブルを作成しました。
私は特に日付を処理するために数値テーブルを作成しました:
_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までの日付ごとに1行の表が作成されます。私は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
_
これにより、サンプルデータの出力は次のようになります。