私は2つのテーブルを持っています:BASE
はマスターであり、フィールドCHILD
を介してID
にリンクされています:左外部結合/ 1:Nリレーション。
これらの2つのテーブルには、2つの日付フィールドstart_date
およびend_date
で定義された履歴データが含まれています。
ただし、BASE
の履歴は、CHILD
の履歴から独立しています。例えば。:
BASE
1 1/1/2000 1/1/2010
1 1/1/2010 31/12/2012
CHILD
1 1/1/2000 31/12/2005
1 31/12/2005 1/1/2010
目標:
特定の日付について、両方のテーブルでアクティブレコードを選択し、1つの結果レコードに表示します。
問題:CHILD
に一致がない場合(例:2011年1月1日)、「行がありません」と表示されますが、BASE
(およびCHILD
- fieldsのNull値)のデータが表示されます。
これは私のクエリです:
SELECT
BASE.*, CHILD.*
FROM
BASE LEFT OUTER JOIN CHILD ON (BASE.ID=CHILD.ID)
WHERE
BASE.ID = '1'
AND BASE.START_DATE <= TO_DATE('1/1/2011', 'DD/MM/YYYY')
AND BASE.END_DATE > TO_DATE('1/1/2011', 'DD/MM/YYYY')
AND CHILD.START_DATE <= TO_DATE('1/1/2011', 'DD/MM/YYYY')
AND CHILD.END_DATE > TO_DATE('1/1/2011', 'DD/MM/YYYY')
CHILD
-WHERE
句の列により、LEFT JOIN
がINNER JOIN
に変換されることはわかっています。私はこのフォーラムで見つけたIS NULL
- trickも試しましたが、それでもまだ苦労しています。
重要な注意:
技術的なセットアップのため、FROM
句を変更することはできないため、WHERE
句を使用して解決策を見つける必要があります…
手伝ってくれますか?
中途半端にエレガントな解決策を見つける前に、それは私にかなりの頭痛の種を引き起こしました:
WITH x AS (
SELECT BASE.id AS b_id
,BASE.start_date AS b_start_date
,BASE.end_date AS b_end_date
,CHILD.id AS c_id
,CHILD.start_date AS c_start_date
,CHILD.end_date AS c_end_date
,CASE WHEN DATE '2011-1-1' BETWEEN CHILD.start_date AND CHILD.end_date
OR CHILD.id IS NULL THEN 0 ELSE 1 END AS c_hit
FROM BASE LEFT OUTER JOIN CHILD ON (BASE.ID=CHILD.ID)
WHERE BASE.ID = 1
AND DATE '2011-1-1' BETWEEN BASE.start_date AND BASE.end_date
)
SELECT b_id, b_start_date, b_end_date
,c_id, c_start_date, c_end_date
FROM x
WHERE x.c_hit = 0
UNION ALL
SELECT b_id, b_start_date, b_end_date
,NULL, NULL, NULL
FROM x
GROUP BY b_id, b_start_date, b_end_date
HAVING min(c_hit) = 1;
もちろん、より良い解決策は、制限を取り除き、基本クエリのJOINを調整することです。 @a_horseが上記のコメントで詩的に表現したように:
oRMフレームワークの危険
とにかく、目の前の質問を解決するには:
行を選択した後、BASE.id
およびBASE.start_date
/BASE.end_date
一致、4つの異なるケースがあります。
これを1つのクエリレベルで解決するのは困難です。ウィンドウ関数とCASE
ステートメントとDISTINCT
を使用してそれを行うことができます。実際、私は基本的なクエリを実行していましたが、それは途方もないものになります。
CTEを使用すると、構文が大幅に簡素化されます。
CTE:
BASE
とid
をフィルターし、日付範囲を指定します。一致しない子を持つ行がまだ含まれています。最後のSELECTでは:
これは、それをいじりたい人のための sqlfiddle.comの作業デモ です。
編集された提案:
結合条件を変更せずに、条件を満たさない場合は、select句の子フィールドをnullにすることができますか?
Select base.*,
case
when child.start_date <= to_date('1/1/2011','dd/mm/yyyy')
and child.end_date > to_date('1/1/2011','dd/mm/yyyy')
then child.field1
else null
end as c_field1
from (...)
where base.start_date (...) and base.end_date (...)
(where句にchild.start_dateまたはend_dateはありません)