documents:proglang:sql:sql-0006
差分
このページの2つのバージョン間の差分を表示します。
| 両方とも前のリビジョン前のリビジョン | |||
| documents:proglang:sql:sql-0006 [2026/05/08 12:19] – ↷ documents:database:sql-0006 から documents:proglang:sql:sql-0006 へページを移動しました。 k896951 | documents:proglang:sql:sql-0006 [2026/05/16 11:35] (現在) – [SQLで各グループ中の最大値を持つレコードに印を付けたい] k896951 | ||
|---|---|---|---|
| 行 1: | 行 1: | ||
| + | ====== 002.SQLで各グループ中の最大値を持つレコードに印を付けたい ====== | ||
| + | 2014年8月29日\\ | ||
| + | Oracle 11g Release 11.1 で説明しています。 | ||
| + | |||
| + | お客さんへの説明でいろいろやったんでついでに自分の資料用に作ってしまった。 | ||
| + | |||
| + | {{description> | ||
| + | |||
| + | ===== 各グループ中で最大値を持つレコードに印を付けたい ===== | ||
| + | |||
| + | |||
| + | |||
| + | 大きな組織では、複数の活動拠点から日毎のデータが送られてきて、それを元に経営や営業の判断に利用する、という事が普通に行われています。\\ | ||
| + | 例えばこんなテーブルがあるとしましょう。 | ||
| + | |||
| + | <code sql> | ||
| + | SQL> DESC TRANTBL | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | |||
| + | |||
| + | |||
| + | SQL> SELECT * FROM TRANTBL WHERE DATE_YM=' | ||
| + | |||
| + | DATE_YM | ||
| + | ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201401 | ||
| + | 201401 | ||
| + | 201401 | ||
| + | 201401 | ||
| + | 201401 | ||
| + | 201401 | ||
| + | 201401 | ||
| + | 201401 | ||
| + | 201401 | ||
| + | 201401 | ||
| + | |||
| + | 10 rows selected. | ||
| + | |||
| + | SQL> | ||
| + | </ | ||
| + | |||
| + | テーブルには日々レコードが追加され続け、ある程度の期間が過ぎると、該当レコードはテーブルから削除されます。 | ||
| + | |||
| + | ですがこのテーブルは以降の話の為に、年月での集約集計が行われた結果が格納されている、という事にしましょう。 | ||
| + | この例は2014年1月の情報ですね。 | ||
| + | |||
| + | |||
| + | 後輩:「先輩、上司から作業指示がありまして。\\ | ||
| + | 先輩:「面倒なのはいやだよ?\\ | ||
| + | 後輩:「面倒なんですよねー。こんな風に、エリアで売り上げ最大の拠点にマークを付けたリストが欲しいそうです。\\ | ||
| + | |||
| + | <code sql> | ||
| + | TOPSALES DATE_YM | ||
| + | -------- ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | | ||
| + | Y 201401 | ||
| + | | ||
| + | Y 201401 | ||
| + | | ||
| + | | ||
| + | Y 201401 | ||
| + | | ||
| + | | ||
| + | | ||
| + | </ | ||
| + | |||
| + | たぶん、データベースにかかわるお仕事をしていると1回はやる事になるお題です。 | ||
| + | |||
| + | ある後輩氏は項目TOPSALESを含むテーブルXを作り、テーブルTRANTBLからXへ必要なレコードをINSERTして、更にテーブルXにUPDATE文でマークを付ける処理実行、という2段階の処理を行いました。\\ | ||
| + | まとめてやろうとすると面倒な事も分割すると楽になるので、そういうのもありなんですが、上記はサブクエリやウインドウ関数を知っていると楽が出来ます。 | ||
| + | |||
| + | ==== サブクエリを使った例 ==== | ||
| + | |||
| + | |||
| + | 先輩:「とりあえずサブクエリの使い方覚えてみよう。\\ | ||
| + | 後輩:「え!?FROM句以外にも使えるんですか!?\\ | ||
| + | 先輩:「少ないデータ量ならこういうやり方がある。\\ | ||
| + | |||
| + | <code sql> | ||
| + | SQL> SELECT | ||
| + | 2 CASE WHEN SALES=(SELECT MAX(SALES) FROM TRANTBL WHERE DATE_YM=M.DATE_YM AND AREACODE=M.AREACODE) THEN ' | ||
| + | 3 , | ||
| + | 4 , | ||
| + | 5 , | ||
| + | 6 , | ||
| + | 7 , | ||
| + | 8 , | ||
| + | 9 FROM TRANTBL M | ||
| + | | ||
| + | | ||
| + | |||
| + | TOP DATE_YM | ||
| + | --- ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201401 | ||
| + | Y | ||
| + | 201401 | ||
| + | Y | ||
| + | 201401 | ||
| + | 201401 | ||
| + | Y | ||
| + | 201401 | ||
| + | 201401 | ||
| + | 201401 | ||
| + | |||
| + | 10 rows selected. | ||
| + | |||
| + | SQL> | ||
| + | </ | ||
| + | |||
| + | 後輩:「おー!!こういう使い方が出来るのかー\\ | ||
| + | 先輩:「もう少しお勉強しようよ…\\ | ||
| + | |||
| + | |||
| + | CASE構文でサブクエリを使っています。CASE構文では | ||
| + | |||
| + | * 『現在のレコードの項目DATE_YMと項目AREACODEを条件にサブクエリを実行しテーブルTRANTBLのレコード中で最大値を取得し』 | ||
| + | * 『現在のレコードのを持つ項目SALESと最大値が同一かどうか』 | ||
| + | |||
| + | を判定しています。\\ | ||
| + | 現在のレコードの項目SALESと最大値が等しいならそのレコードは最大値を持つレコードとなりますので、印である‘Y' | ||
| + | |||
| + | 構文的には1レコード毎にサブクエリが実行されます。なので項目DATE_YM=' | ||
| + | …データベースの内部処理的には、最適化処理が行われ同条件のサブクエリは1回だけの実行になります。 | ||
| + | |||
| + | |||
| + | ==== ウインドウ関数を使った例 ==== | ||
| + | |||
| + | 先輩:「今度はウインドウ関数を使う例。どっちかというとこっちを覚えたほうがいい。\\ | ||
| + | 後輩:「なら最初にこっち示してくれれば良いじゃないですかーヤダー\\ | ||
| + | 先輩:「最初のはサブクエリの復習がてら例示しただけだもの。今度はウインドウ関数の使用例。\\ | ||
| + | |||
| + | <code sql> | ||
| + | SQL> SELECT | ||
| + | 2 CASE WHEN SALES = MAX(SALES) OVER (PARTITION BY DATE_YM, AREACODE) THEN ' | ||
| + | 3 , | ||
| + | 4 , | ||
| + | 5 , | ||
| + | 6 , | ||
| + | 7 , | ||
| + | 8 , | ||
| + | 9 FROM TRANTBL | ||
| + | | ||
| + | | ||
| + | |||
| + | TOP DATE_YM | ||
| + | --- ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201401 | ||
| + | Y | ||
| + | 201401 | ||
| + | Y | ||
| + | 201401 | ||
| + | 201401 | ||
| + | Y | ||
| + | 201401 | ||
| + | 201401 | ||
| + | 201401 | ||
| + | |||
| + | 10 rows selected. | ||
| + | |||
| + | SQL> | ||
| + | </ | ||
| + | |||
| + | CASE構文は一緒なんですが、今度は直接集計関数MAXを使っています。そして、その後ろ、OVER 句以降にPARTITION BY 句があります。 | ||
| + | |||
| + | これは、この集計関数MAXの集計対象レコードが、 | ||
| + | * 『現在のSELECT句で対象となるレコードセット( この例ならテーブルTRANTBLの条件DATE_YM=' | ||
| + | * 『現在のレコード中の項目DATE_YMと項目AREACODEと同じ値を持っているレコード』 | ||
| + | |||
| + | である事を指定します。集計関数MAXの集計範囲を指定している訳で、サブクエリで集計する範囲を指定してるのとそれほど変わらないように見えます。 | ||
| + | |||
| + | でも、データベースシステム的には結構な違いがあるんですよ。 | ||
| + | |||
| + | |||
| + | ==== サブクエリ版とウインドウ関数版の違い ==== | ||
| + | |||
| + | |||
| + | 後輩:「どっちも結果が一緒なら二つ覚えなくても良いんじゃないですかね。\\ | ||
| + | 先輩:「正確には結果は一緒じゃないんだよね。で、データが少量ならサブクエリの方がやってる事がわかり易い。データ量が大きくなるとウインドウ関数版じゃなきゃ駄目かもしれない。\\ | ||
| + | 後輩:「納得いく説明が無いと許しませんよ?\\ | ||
| + | |||
| + | |||
| + | では、先のサブクエリ版の実行計画を覗いてみましょう。 | ||
| + | |||
| + | <code sql> | ||
| + | SQL> EXPLAIN PLAN FOR | ||
| + | 2 SELECT | ||
| + | 3 CASE WHEN SALES=(SELECT MAX(SALES) FROM TRANTBL WHERE DATE_YM=M.DATE_YM AND AREACODE=M.AREACODE) THEN ' | ||
| + | 4 , | ||
| + | 5 , | ||
| + | 6 , | ||
| + | 7 , | ||
| + | 8 , | ||
| + | 9 , | ||
| + | | ||
| + | | ||
| + | | ||
| + | |||
| + | Explained. | ||
| + | |||
| + | SQL> SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY()); | ||
| + | |||
| + | PLAN_TABLE_OUTPUT | ||
| + | -------------------------------------------------------------------------------- | ||
| + | Plan hash value: 4245077362 | ||
| + | |||
| + | ------------------------------------------------------------------------------ | ||
| + | | Id | Operation | ||
| + | ------------------------------------------------------------------------------ | ||
| + | | 0 | SELECT STATEMENT | ||
| + | | 1 | SORT AGGREGATE | ||
| + | |* 2 | TABLE ACCESS FULL| TRANTBL | 1 | 31 | | ||
| + | |* 3 | TABLE ACCESS FULL | TRANTBL | 10 | 610 | | ||
| + | ------------------------------------------------------------------------------ | ||
| + | |||
| + | |||
| + | PLAN_TABLE_OUTPUT | ||
| + | -------------------------------------------------------------------------------- | ||
| + | Predicate Information (identified by operation id): | ||
| + | --------------------------------------------------- | ||
| + | |||
| + | 2 - filter(" | ||
| + | 3 - filter(" | ||
| + | |||
| + | Note | ||
| + | ----- | ||
| + | - dynamic sampling used for this statement | ||
| + | |||
| + | 20 rows selected. | ||
| + | |||
| + | SQL> | ||
| + | </ | ||
| + | |||
| + | 後輩:「Id 2で読み出した結果をId 1で集計し、Id 0でId 1とId 3をマージしてるんですね。\\ | ||
| + | 先輩:「そういう理解でいいと思うよ。\\ | ||
| + | |||
| + | 「TABLE ACCESS FULL」が2回現れている事がわかります。OracleさんがテーブルTRANTBL全体読み込みを2回繰り返しますよ、と言ってるわけです。\\ | ||
| + | そして、「2 - filter(..)」を見ると、項目DATE_YMと項目AREACODEで集計条件を指定しているようです。 | ||
| + | |||
| + | * Id 2 のテーブル読み込みは、項目DATE_YMと項目AREACODEでグループ化されたレコードに対しての集計関数MAXの値を取得するId 1の処理のため、 | ||
| + | * Id 3 のテーブル読み込みは、Id 1 の結果を使った、印付与判定処理のため、 | ||
| + | |||
| + | と考えられます。 | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | 次はウインドウ関数版の実行計画です。 | ||
| + | |||
| + | <code sql> | ||
| + | SQL> EXPLAIN PLAN FOR | ||
| + | 2 SELECT | ||
| + | 3 CASE WHEN SALES = MAX(SALES) OVER (PARTITION BY DATE_YM, AREACODE) THEN ' | ||
| + | 4 , | ||
| + | 5 , | ||
| + | 6 , | ||
| + | 7 , | ||
| + | 8 , | ||
| + | 9 , | ||
| + | | ||
| + | | ||
| + | | ||
| + | |||
| + | Explained. | ||
| + | |||
| + | SQL> SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY()); | ||
| + | |||
| + | PLAN_TABLE_OUTPUT | ||
| + | -------------------------------------------------------------------------------- | ||
| + | Plan hash value: 1075596177 | ||
| + | |||
| + | ------------------------------------------------------------------------------ | ||
| + | | Id | Operation | ||
| + | ------------------------------------------------------------------------------ | ||
| + | | 0 | SELECT STATEMENT | ||
| + | | 1 | WINDOW SORT | ||
| + | |* 2 | TABLE ACCESS FULL| TRANTBL | 10 | 610 | | ||
| + | ------------------------------------------------------------------------------ | ||
| + | |||
| + | Predicate Information (identified by operation id): | ||
| + | |||
| + | PLAN_TABLE_OUTPUT | ||
| + | -------------------------------------------------------------------------------- | ||
| + | --------------------------------------------------- | ||
| + | |||
| + | 2 - filter(" | ||
| + | |||
| + | Note | ||
| + | ----- | ||
| + | - dynamic sampling used for this statement | ||
| + | |||
| + | 18 rows selected. | ||
| + | |||
| + | SQL> | ||
| + | </ | ||
| + | |||
| + | 「TABLE ACCESS FULL」が1回しかありません。また、項目DATE_YMと項目AREACODEをキーに集計を行っている様子もありません。\\ | ||
| + | これは、Id 2 のテーブル読み込みを行いながら同時に必要な集計を行っているためです。集約集計のためのキーはPARTITION BY 句で判明しているので、Id 1でキー毎の集計を取っています。 | ||
| + | その為1回のテーブル読み込みで必要な処理が終わっています。 | ||
| + | |||
| + | 先輩:「1回の読み込みで必要な集計処理も同時に済ませている。なので2回読み込む必要がない。\\ | ||
| + | 後輩:「言われてみれば、わざわざ別けて読み直ししなくても処理できますもんね。\\ | ||
| + | 先輩:「それに、『SELECT * FROM TRNTBL WHERE DATE_YM=' | ||
| + | 後輩:「更に別の判定をサブクエリを使って追加すると、対応するTABLE ACCESS FULLが発生するから、知っていないと大変な結果を招く場合があるって事ですね。\\ | ||
| + | |||
| + | |||
| + | 後輩:「でもウインドウ関数版はレビュー時の説明で苦労しそうだなぁ……\\ | ||
| + | 先輩:「ウインドウ関数は知らない人への説明が面倒だけど、ウインドウ関数を使うとデータベースが1回で読み込みを終えてくれるようになる、と言えばきっと分かってくれると思うよ。\\ | ||
| + | |||
| + | |||
| + | ===== 印を付けるレコードが一意に決まらない場合の対処例 ===== | ||
| + | |||
| + | そして、やっぱり一度はやる失敗。 | ||
| + | |||
| + | |||
| + | 後輩:「項目SALESに重複が発生してしまいました!なので複数の印がついちゃいます!\\ | ||
| + | 先輩:「そらそうだ、合計値が一緒になる事を想定出来ないやつがおかしい。\\ | ||
| + | 後輩:「orz\\ | ||
| + | |||
| + | 今回の処理対象はこれになります。 | ||
| + | |||
| + | <code sql> | ||
| + | SQL> SELECT * FROM TRANTBL WHERE DATE_YM=' | ||
| + | |||
| + | DATE_YM | ||
| + | ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | |||
| + | 9 rows selected. | ||
| + | |||
| + | SQL> | ||
| + | </ | ||
| + | |||
| + | これにPARTITION BY句を使ったクエリを適用すると、当然ながら AREACODE=10、BRANCHCODE=10102、の2レコードに印がついてしまいます。 | ||
| + | |||
| + | <code sql> | ||
| + | SQL> SELECT | ||
| + | 2 CASE WHEN SALES = MAX(SALES) OVER (PARTITION BY DATE_YM, AREACODE) THEN ' | ||
| + | 3 , | ||
| + | 4 , | ||
| + | 5 , | ||
| + | 6 , | ||
| + | 7 , | ||
| + | 8 , | ||
| + | 9 FROM TRANTBL | ||
| + | | ||
| + | | ||
| + | |||
| + | TOP DATE_YM | ||
| + | --- ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201404 | ||
| + | Y | ||
| + | Y | ||
| + | 201404 | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | |||
| + | 9 rows selected. | ||
| + | |||
| + | SQL> | ||
| + | </ | ||
| + | |||
| + | 先輩:「一意に決めるコードが無いならこうなるしかないよね。\\ | ||
| + | 後輩:「何とかインチキできませんかね?\\ | ||
| + | 先輩:「水曜どうでしょう再開したんだってねぇ。…ソートを行って、常にレコードの並びを一意に出来るなら手はある。\\ | ||
| + | |||
| + | |||
| + | ==== ウインドウ関数LAGを使った例 ==== | ||
| + | |||
| + | 以下、OVER 句以降で指定する | ||
| + | * PARTITION BY 句で指定する項目AREACODE、項目BRANCHCODE、で指定した範囲 | ||
| + | * ORDER BY 句で指定する項目SALES(降順)、項目AREACODE、項目BRANCHCODE、項目ITEMCODE、でソート順を指定 | ||
| + | |||
| + | のレコードセットを適用対象範囲とした、ウインドウ関数LAGで判定し印を付ける例です。 | ||
| + | |||
| + | <code sql> | ||
| + | SQL> SELECT | ||
| + | 2 CASE WHEN LAG(SALES, | ||
| + | 3 , | ||
| + | 4 , | ||
| + | 5 , | ||
| + | 6 , | ||
| + | 7 , | ||
| + | 8 , | ||
| + | 9 FROM TRANTBL | ||
| + | | ||
| + | | ||
| + | |||
| + | TOP DATE_YM | ||
| + | --- ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | Y | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | |||
| + | 9 rows selected. | ||
| + | |||
| + | SQL> | ||
| + | </ | ||
| + | |||
| + | ウインドウ関数LAGは、現在のレコードセットの直前のレコードにあった項目の値を取得します。LAG(SALES, | ||
| + | |||
| + | 上記の例で、項目DATE_YM=' | ||
| + | * レコードセット2番目のレコードでLAG(SALES, | ||
| + | * レコードセット1番目のレコードでLAG(SALES, | ||
| + | |||
| + | ORDER BY句で最初に項目SALESを降順で指定したのであれば、最大値を持つレコードが先頭に集まります。 | ||
| + | そして、最大値の同じレコードが並んでいたとしてもウインドウ関数LAGを使う事で、1番目なのか、それ以外なのかを判定できるので、先頭にあるレコードだけに印を付けてしまえばいいのです。 | ||
| + | |||
| + | |||
| + | 後輩:「並びがちょっと変わっちゃいましたね。できれば最初の並びにしたいですねー\\ | ||
| + | 先輩:「完全には無理だけど、こんな感じでソートしなおせばいいと思う。\\ | ||
| + | |||
| + | <code sql> | ||
| + | SQL> SELECT | ||
| + | 2 CASE WHEN LAG(SALES, | ||
| + | 3 , | ||
| + | 4 , | ||
| + | 5 , | ||
| + | 6 , | ||
| + | 7 , | ||
| + | 8 , | ||
| + | 9 FROM TRANTBL | ||
| + | | ||
| + | | ||
| + | | ||
| + | |||
| + | TOP DATE_YM | ||
| + | --- ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | |||
| + | 9 rows selected. | ||
| + | |||
| + | SQL> | ||
| + | </ | ||
| + | |||
| + | 元のクエリでもORDER BY 句がないので、この出力結果は環境依存となります。\\ | ||
| + | たまたま並びがそうなった、と理解してください。“完全には無理”というのはそういうことです。 | ||
| + | |||
| + | |||
| + | ==== ORDER BY 句指定時の注意点 ==== | ||
| + | |||
| + | |||
| + | 後輩:「ウインドウ関数LAGを使うときのORDER BY 句なんですが、項目SALESだけでも良いんじゃないでしょうか。\\ | ||
| + | 先輩:「駄目。それだと実行の度にLAG(SALES, | ||
| + | 後輩:「kwsk\\ | ||
| + | 先輩:「項目SALESだけの指定だと、ソートの結果が不定になってしまうから。なので、ORDER BY 句の指定でソート結果を一意にしている。\\ | ||
| + | |||
| + | |||
| + | SELECT文実行時そのレコードセットの出力順序は不定です。\\ | ||
| + | ORDER BY 句を指定して出力順序を決めるのですが、指定が足りなかったりすると、その足りない部分での出力順が不定となります。これは環境に依存する部分です。 | ||
| + | |||
| + | 例えば、以下のクエリ | ||
| + | <code sql> | ||
| + | SELECT * FROM TRANTBL | ||
| + | WHERE DATE_YM=' | ||
| + | ORDER BY SALES DESC, AREACODE, BRANCHCODE | ||
| + | </ | ||
| + | |||
| + | だと、次のような2通りの結果が発生する可能性があります。 | ||
| + | |||
| + | <code sql> | ||
| + | DATE_YM | ||
| + | ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | |||
| + | |||
| + | DATE_YM | ||
| + | ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | </ | ||
| + | |||
| + | OVER 句以降の記述に現れるORDER BY 句も同様で、ソート順を決める項目の指定に不足があればソート結果が異なる可能性がありますので、以下の様に印が付くレコードが一意に決まらず不安定に見える場合があります。 | ||
| + | |||
| + | <code sql> | ||
| + | TOP DATE_YM | ||
| + | --- ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | Y | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | |||
| + | TOP DATE_YM | ||
| + | --- ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | Y | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | </ | ||
| + | |||
| + | |||
| + | 上記の不定動作を排除するため、ORDER BY 句には以下のクエリの様に、 | ||
| + | <code sql> | ||
| + | SELECT * FROM TRANTBL | ||
| + | WHERE DATE_YM=' | ||
| + | ORDER BY SALES DESC, AREACODE, BRANCHCODE, ITEMCODE | ||
| + | </ | ||
| + | |||
| + | 必要な項目を記述して明示的に結果を一意にさせます。 | ||
| + | |||
| + | < | ||
| + | DATE_YM | ||
| + | ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | </ | ||
| + | |||
| + | 先輩:「楽そうだからターゲットを先頭のレコードとしてみただけなので、好きな手段で決めればいいと思うよ。\\ | ||
| + | 後輩:「考えるの面倒なので、一意に決めるキーを付ける様にお願いしてみます。\\ | ||
| + | 先輩:「じゃあ最初からそうして。今までの手間はなんだったのよ……\\ | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | ===== 印が付くレコードだけ欲しい場合の対処例 ===== | ||
| + | |||
| + | 後輩:「上司が、印が付くレコードだけ欲しいといってきたんですけど。\\ | ||
| + | 先輩:「まぁそうだよね。トップだけ見たいって言われるのも想像できるよね。\\ | ||
| + | 後輩:「WHERE句にウインドウ関数使えないみたいなんで、該当レコードだけってのがやれないんですけど…\\ | ||
| + | 先輩:「後輩ってさ、本当に応用力が無い子よねぇ……\\ | ||
| + | |||
| + | |||
| + | 以下のような結果、 | ||
| + | <code sql> | ||
| + | TOP DATE_YM | ||
| + | --- ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | Y | ||
| + | 201404 | ||
| + | </ | ||
| + | |||
| + | を、必要なレコードだけしにして | ||
| + | |||
| + | <code sql> | ||
| + | DATE_YM | ||
| + | ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | </ | ||
| + | |||
| + | としたい訳ですね。 | ||
| + | |||
| + | 難しく考えなくてもサブクエリで解決すればいいんです。既にどのレコードが対象か分かる印をつける事ができているんですから。 | ||
| + | |||
| + | <code sql> | ||
| + | SQL> | ||
| + | 2 FROM ( | ||
| + | 3 SELECT | ||
| + | 4 CASE WHEN LAG(SALES, | ||
| + | 5 ,DATE_YM | ||
| + | 6 ,AREACODE | ||
| + | 7 ,BRANCHCODE | ||
| + | 8 ,ITEMCODE | ||
| + | 9 ,VOLUME | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | 14 ) S | ||
| + | 15 WHERE S.TOPSALES=' | ||
| + | 16 ; | ||
| + | |||
| + | DATE_YM | ||
| + | ------------------ ---------- ---------- ------------ ---------- ---------- | ||
| + | 201404 | ||
| + | 201404 | ||
| + | 201404 | ||
| + | |||
| + | 3 rows selected. | ||
| + | |||
| + | SQL> | ||
| + | </ | ||
| + | |||
| + | 先輩:「気になるならSQLの実行計画を調べてみればいいよ。\\ | ||
| + | 後輩:「なんかだまされた気分。サブクエリ使うなって言われたその直後に使ってるし……\\ | ||
| + | 先輩:「確定した情報を利用している、実に分かり易いサンプルだと思うけどなぁ。\\ | ||
| + | |||
| + | |||
| + | {{tag> SQL Oracle database 技術資料}} | ||
documents/proglang/sql/sql-0006.txt · 最終更新: by k896951
