これはGROUP BY
とKEEP(DENSE_RANK FIRST)
を使用して、(サブクエリと外側のクエリを必要とせずに)データ上ワンパスで行うことができます。
最初にいくつかのハウスキーピング。
- 二重引用符で囲まれた名前を使用しない限り、表名と列名にはスペースを入れることはできません。大文字と小文字は区別されません。
- 日付の列は数値形式であるようですが、これは非常に貧弱な方法です。 20151490(14番目の月の90日目)などの入力がDBに格納されないようにするにはどうすればよいですか?すべての日付を日付として保存するべきです(SHOULD)。しかし、それらを正確にその形式で保存することで、正しい順序比較が可能になります(ただし、これは偶然であり、信頼されるべきではありません)。それはあなたの質問の要点ではないので、私はそのままデータを使用しました。
- なぜ結合が必要ですか?最初のテーブルには
cust_id
が含まれていてはなりません。これは、データベース設計の第2の通常の形式に違反しています。実際に、最初のテーブルにその列がある場合は、2番目のテーブルが必要ない、または結合が必要ではありません。 (cust_id
が最初のテーブルにない場合は、参加が必要ですが、タイトルに関わらず、参加することではなく、正しい行を選ぶことが本当に問題なので、脇に置いておきます。
- 最初のテーブルには、同じアカウントに関連付けられた2つの
cust_id
,3,4があります(2番目のテーブルと矛盾します)。私はそれが誤植であり、実際には4は3でなければならないと仮定しますが、これは2番目の正規形がなぜ重要かを正確に示しています。あなたはcust_id
を最初のテーブルに入れてはいけません。
再フォーマットされた要件の鍵は、条件付き注文です。指定されたアカウントについて、ファイル上のすべてのカードがキャンセルされた場合、またはキャンセルされていないカードがある場合は、最も早いカードを選択してください。join_date
ただし、アカウントに両方の種類のカードが混在している場合は、キャンセルされていない最も早いカードを選んでください。 SQLでは、2つの式(2つの式のうち、SECONDがjoin_date
)で合成順序付けを行うことができます。第1の基準は「条件付き」部分である。以下のソリューションでは、式CASE when cancel_date = 0 then 0 end
を使用します。つまり、キャンセルされていないカードにはフラグ0が設定され、取り消されたカードにはフラグNULL
(の式にELSE
の部分がない場合のデフォルト)が設定されます。デフォルトでNULL
は注文の最後になります(デフォルトではasc
)。したがって、すべてのカードがまだ有効な場合、それらはすべてフラグ0を持ち、このフラグによる順序は関係ありません。すべてがキャンセルされた場合、フラグはすべてNULLになります。したがって、このフラグの順序は問題になりません。しかし、有効なものと無効なものがある場合、有効なものが最初に来るので、最も早い日付は有効なカードからのみ選択されます。
then 0
(フラグ値0)は無関係です。私はそれを1、または文字列(then 'a'
)にすることができ、 "条件付き順序付け"は同じ理由で同様に機能します。有効なカードにはNULL
ではないものを添付し、キャンセルされたカードにはNULL
を添付します。それは重要なことのすべてです。
これは、ゴードン氏が解決策を作るために必要な変更点です。しかし、このような場合は、パフォーマンスが重要な場合(特に、顧客、アカウント、クレジットカードのファイルが非常に多い場合など)には、KEEP(DENSE_RANK FIRST)
の方が好きです。
with
customer_card (cust_id , cust_acct , card_no , join_date , cancel_date) as (
select 1, 10001, 'E100001', 20150501, 20160101 from dual union all
select 1, 10001, 'E100002', 20151001, 0 from dual union all
select 2, 10002, 'E100003', 20150101, 20160601 from dual union all
select 3, 10003, 'E100004', 20150201, 0 from dual union all
select 3, 10003, 'E100005', 20160101, 0 from dual
)
-- end of test data; actual solution begins HERE
select cust_id, cust_acct,
min(card_no) keep (dense_rank first
order by case when cancel_date = 0 then 0 end, join_date) as card_no,
min(join_date) keep (dense_rank first
order by case when cancel_date = 0 then 0 end, join_date) as join_date,
min(cancel_date) keep (dense_rank first
order by case when cancel_date = 0 then 0 end, join_date) as cancel_date
from customer_card
group by cust_id, cust_acct
order by cust_id, cust_acct -- ORDER BY is optional
;
出力:
CUST_ID CUST_ACCT CARD_NO JOIN_DATE CANCEL_DATE
--------- ---------- ------- --------- -----------
1 10001 E100002 20151001 0
2 10002 E100003 20150101 20160601
3 10003 E100004 20150201 0
お客様のためにすべてのカードがキャンセルされると、何が表示されるのでしょうか? – hemalp108
最も早い参加を表示 – crchin