MW211 EXIT

devlog
Oracle/掛け算(総積)
2019年06月26日
各行の値を足し算する(総和)のは以下の通り、基本中の基本だ。
┌──────────────────────────────────────┐
│SELECT SUM("数")                                                            │
│    FROM "表";                                                              │
└──────────────────────────────────────┘

じゃ、掛け算にできるか?(総積)
┌──────────────────────────────────────┐
│SELECT ROUND(EXP(SUM(LN("数"))))                                            │
│    FROM "表";                                                              │
└──────────────────────────────────────┘
こんな感じでてきてしまうのである。

自前の表を使って確認してみると確かに「2×3=6」という結果になる
┌──────────────────────────────────────┐
│WITH "表" AS (                                                              │
│         SELECT *                                                           │
│             FROM (                                                         │
│                           SELECT 2 AS "数" FROM DUAL                       │
│                 UNION ALL SELECT 3 AS "数" FROM DUAL                       │
│             )                                                              │
│     )                                                                      │
│SELECT ROUND(EXP(SUM(LN("数"))))                                            │
│    FROM "表";                                                              │
└──────────────────────────────────────┘

やっていることは以下の通り
(1) 「LN()」で一旦、自然対数(log)に変換する
(2) 「SUM()」でそれらを足し算する(総和を求める)
(3) その結果を「EXP()」で元に戻す
(4) 計算の過程で端数(ほんの小さい誤差)が出るので「ROUND()」で四捨五入し調整
分類:Oracle
Oracle/一つ前のレコード(埋める編)
2019年05月21日
一つ前のレコードを参照するには、LAG()関数を使う。
じゃ、NULLの場合に一つ前の値を補填してみようとする。
┌──────────────────────────────────────┐
│SELECT NVL("列",                                                            │
│           LAG("列") OVER(PARTITION BY "グループ列"                         │
│                          ORDER BY     "ソート列"   ASC)                    │
│       ) AS "列"                                                            │
│    FROM "表";                                                              │
└──────────────────────────────────────┘
でもこれだと、本当に一つ前しか埋めてくれない。
  列        列
  ----      ----
  1     →  1
  NULL      1     ←ここしか効かない
  NULL      NULL  ←ここが効かない
  ----      ----

なんと、これを一発で解決してくれる優れものがある。
「IGNORE NULLS」オプションだ。
以下のように、LAG()関数につけてあげるだけ。
┌──────────────────────────────────────┐
│SELECT NVL("列",                                                            │
│           LAG("列" IGNORE NULLS) OVER(PARTITION BY "グループ列"            │
│                                       ORDER BY     "ソート列"   ASC)       │
│       ) AS "列"                                                            │
│    FROM "表";                                                              │
└──────────────────────────────────────┘
お望み通りの結果になる。
  列        列
  ----      ----
  1     →  1
  NULL      1
  NULL      1
  ----      ----
分類:Oracle
Oracle/空文字への置換は注意
2018年07月25日
REPLACE()は検索文字に一致した部分を置換する関数だが
TRANSLATE()は検索文字のいずれかに一致した部分を置換してくれる関数だ。
┌──────────────────────────────────────┐
│TRANSLATE(列, '0123456789', ' ')                                            │
└──────────────────────────────────────┘
数値だけを空白に置換する場合には、以下のようにすればよい。

┌──────────────────────────────────────┐
│TRANSLATE(列, '0123456789', '')                                       →NULL│
└──────────────────────────────────────┘
でも、空文字に置換してしまうと、結果は(如何なる場合も)NULLになってしまう。
これは空文字をNULLと同一とみなすOracle特有の事情のようだ。

従って、一回適当な文字(ここでは「0」)に置換して統一した上で
REPLACE()でまとめて置換するのがよいようだ。
┌──────────────────────────────────────┐
│REPLACE(TRANSLATE(列, '0123456789', '0'), '0', '')                          │
└──────────────────────────────────────┘
REPLACE()の場合は、空文字に置換しても問題はない。

数字のみで構成されるかを確認する場合には、TRIM()を使って簡素化できる。
┌──────────────────────────────────────┐
│TRIM(TRANSLATE(列, '0123456789', ' '))                                      │
└──────────────────────────────────────┘
NULLだった場合(IS NULLが真の場合)、数字のみだった訳だ。
→置換して空欄(=NULL)となった訳だから
分類:Oracle
Oracle/プレースホルダのもう一つの利点
2018年07月04日
プレースホルダを使用する利点はSQLインジェクション対策以外に
SQL文の画一化により解析を共有できるという点もあるよ。

以下は別ものとして解析されてしまうが…
・「SELECT * FRON 表 WHERE 列 = 1」
・「SELECT * FRON 表 WHERE 列 = 2」

以下にすれば、一つとして解析されて実績も増えるよということ。
・「SELECT * FRON 表 WHERE 列 = %」
分類:Oracle
Oracle/年月表(年度版)
2018年02月06日
年度版(4月~翌3月)。
┌──────────────────────────────────────┐
│WITH "年月D" AS (                                                          │
│    SELECT "年",                                                            │
│           "月",                                                            │
│           COUNT(*) AS "値"                                                 │
│        FROM (                                                              │
│            SELECT "年"                                                     │
│                   "月"                                                     │
│                FROM "表"                                                   │
│        )                                                                   │
│        GROUP BY "年",                                                      │
│                 "月"                                                       │
│)                                                                           │
│SELECT *                                                                    │
│    FROM (                                                                  │
│        SELECT CASE                                                         │
│                 WHEN "カレンダM"."月" < 4 THEN "カレンダM"."年" - 1      │
│                 ELSE                            "カレンダM"."年"          │
│               END AS "年度",                                               │
│               CASE                                                         │
│                 WHEN "カレンダM"."月" < 4 THEN "カレンダM"."月" + 12     │
│                 ELSE                            "カレンダM"."月"          │
│               END AS "月",                                                 │
│               NVL("年月D"."値", 0) AS "値"                                │
│            FROM (                                                          │
│                SELECT "年"."年",                                           │
│                       "月"."月"                                            │
│                    FROM (                                                  │
│                        SELECT 開始年度 + ROWNUM - 1  AS "年"               │
│                            FROM DUAL                                       │
│                                CONNECT BY ROWNUM <= 終了年度 - 開始年度 + 2│
│                    ) "年"                                                  │
│                        CROSS JOIN (                                        │
│                            SELECT ROWNUM AS "月"                           │
│                                FROM DUAL CONNECT BY ROWNUM <= 12           │
│                        ) "月"                                              │
│            ) "カレンダM"                                                  │
│                LEFT JOIN "年月D"                                          │
│                  ON  "年月D"."年" = "カレンダM"."年"                     │
│                  AND "年月D"."月" = "カレンダM"."月"                     
│    ) "集計D"                                                              │
│     PIVOT (                                                                │
│        SUM("値") FOR "月" IN ( 4 AS  "4月",                                │
│                                5 AS  "5月",                                │
│                                6 AS  "6月",                                │
│                                7 AS  "7月",                                │
│                                8 AS  "8月",                                │
│                                9 AS  "9月",                                │
│                               10 AS "10月",                                │
│                               11 AS "11月",                                │
│                               12 AS "12月",                                │
│                               13 AS  "1月",                                │
│                               14 AS  "2月",                                │
│                               15 AS  "3月")                                │
│    ) "ピボット"                                                            │
│    WHERE "年度" BETWEEN 開始年度 AND 終了年度                              │
│    ORDER BY "年度" ASC;                                                    │
└──────────────────────────────────────┘
  ・「開始年度」と「終了年度」の部分はそれぞれの数値に置き換える
  ・「年月D」の中身を変えれば、元データをいろいろなものにできる
分類:Oracle
Oracle/年月表
2018年02月05日
ピボット集計にて、縦軸に年、横軸に1~12月のマトリクス表を出力するSQL文。
┌──────────────────────────────────────┐
│WITH "年月D" AS (                                                          │
│    SELECT "年",                                                            │
│           "月",                                                            │
│           COUNT(*) AS "値"                                                 │
│        FROM (                                                              │
│            SELECT "年"                                                     │
│                   "月"                                                     │
│                FROM "表"                                                   │
│        )                                                                   │
│        GROUP BY "年",                                                      │
│                 "月"                                                       │
│)                                                                           │
│SELECT *                                                                    │
│    FROM (                                                                  │
│        SELECT "カレンダM"."年",                                           │
│               "カレンダM"."月",                                           │
│               NVL("年月D"."値", 0) AS "値"                                │
│            FROM (                                                          │
│                SELECT "年"."年",                                           │
│                       "月"."月"                                            │
│                    FROM (                                                  │
│                        SELECT 開始年 + ROWNUM - 1  AS "年"                 │
│                            FROM DUAL                                       │
│                                 CONNECT BY ROWNUM <= 終了年 - 開始年 + 1   │
│                    ) "年"                                                  │
│                        CROSS JOIN (                                        │
│                            SELECT ROWNUM AS "月"                           │
│                                FROM DUAL CONNECT BY ROWNUM <= 12           │
│                        ) "月"                                              │
│            ) "カレンダM"                                                  │
│                LEFT JOIN "年月D"                                          │
│                  ON  "年月D"."年" = "カレンダM"."年"                     │
│                  AND "年月D"."月" = "カレンダM"."月"                     │
│    ) "集計D"                                                              │
│    PIVOT (                                                                 │
│        SUM("値") FOR "月" IN ( 1 AS  "1月",                                │
│                                2 AS  "2月",                                │
│                                3 AS  "3月",                                │
│                                4 AS  "4月",                                │
│                                5 AS  "5月",                                │
│                                6 AS  "6月",                                │
│                                7 AS  "7月",                                │
│                                8 AS  "8月",                                │
│                                9 AS  "9月",                                │
│                               10 AS "10月",                                │
│                               11 AS "11月",                                │
│                               12 AS "12月")                                │
│    ) "ピボット"                                                            │
│    ORDER BY "年" ASC;                                                      │
└──────────────────────────────────────┘
  ・「開始年」と「終了年」の部分はそれぞれの数値に置き換える
  ・「年月D」の中身を変えれば、元データをいろいろなものにできる
分類:Oracle
Oracle/一つ前のレコード
2018年02月02日
一つ前のレコードを参照するには、LAG()関数を使う。こんな感じ。
┌──────────────────────────────────────┐
│SELECT LAG("列") OVER(PARTITION BY "グループ列"                             │
│                      ORDER BY     "ソート列"   ASC) AS "列"                │
│    FROM "表";                                                              │
└──────────────────────────────────────┘
その列だけ局地的に一つ前のレコードを取得するような感じになる。
なお、一つまえのレコードがない場合には、「NULL」になる。

では、結合した場合はどうか?
┌──────────────────────────────────────┐
│SELECT LAG("副表"."列") OVER(PARTITION BY "副表"."グループ列"               │
│                             ORDER BY     "副表"."ソート列"   ASC) AS "列"  │
│    FROM "主表"                                                             │
│        LEFT JOIN "副表"                                                    │
│          ON "副表"."主キー" = "主表"."外部キー";                           │
└──────────────────────────────────────┘
うっかりこういうのはNG。予期せぬ「NULL」が多発する。
なぜなら、LAG()関数はあくまで直前の母集団の一つ前のレコードを探し出してくるので
この場合、"副表"側全部の中の一つ前のレコードとはならないのだ。

とはいえ、以下のようなのはNG。
┌──────────────────────────────────────┐
│SELECT "副表"."列"                                                          │
│    FROM "主表"                                                             │
│        LEFT JOIN "副表" "中継副表"                                         │
│            LEFT JOIN "副表"                                                │
│              ON "副表"."主キー"                                            │
│       = LAG("中継副表"."主キー") OVER(PARTITION BY "副表"."グループ列"     │
│                                       ORDER BY     "副表"."ソート列"   ASC)│
│          ON "中継副表"."主キー" = "主表"."外部キー";                       │
└──────────────────────────────────────┘
「ORA-30483: ここでウィンドウ・ファンクションは使用できません。」エラーとなる。

なので、副問合せの時点で解決してしまうのがよいようだ。
┌──────────────────────────────────────┐
│SELECT "副表"."列"                                                          │
│    FROM "主表"                                                             │
│        LEFT JOIN (                                                         │
│            SELECT "主キー",                                                │
│                   LAG("列") OVER(PARTITION BY "グループ列"                 │
│                                  ORDER BY     "ソート列"   ASC) AS "列"    │
│                FROM "副表"                                                 │
│        ) "副表"                                                            │
│          ON "副表"."主キー" = "主表"."外部キー";                           │
└──────────────────────────────────────┘
本当にこれがベストなのかは不安だが。
分類:Oracle
Oracle/SERVEROUTPUT
2017年10月11日
┌───────────┬──────────────────────────┐
│SET SERVEROUTPUT ON;  │DBMS_OUPUT()の出力をONにする                        │
├───────────┼──────────────────────────┤
│SET SERVEROUTPUT OFF; │DBMS_OUPUT()の出力をOFFにする                       │
├───────────┼──────────────────────────┤
│SHOW SERVEROUTPUT;    │設定状況を確認する                                  │
└───────────┴──────────────────────────┘
SQL Developerで、ONして、終了するとOFFに戻る。
そのセッション中のみ設定変更が有効となるようだ。
分類:Oracle
Oracle/ストアドプロシージャでの更新
2017年10月10日
(SQL Developerで)ストアドプロシージャを実行して、いきなり更新してしまうと、
WHERE句の誤記で大変なことになりかねない(全件更新とか)。

そこで、ワンクッションを置く方法。
┌──────────────────────────────────────┐
│CREATE OR REPLACE PROCEDURE "ストアドプロシージャ"                          │
│IS                                                                          │
│    "更新数" NUMBER := 0;                                                   │
│BEGIN                                                                       │
│    UPDATE "表"                                                             │
│        SET "列" = 値                                                       │
│        WHERE "キー" = 値;                                                  │
│    "更新数" := sql%rowcount;                                               │
│    DBMS_OUTPUT.PUT_LINE('更新件数=' || "更新数");                         │
│    RETURN;                                                                 │
│    EXCEPTION                                                               │
│        WHEN OTHERS THEN                                                    │
│            RETURN;                                                         │
│END;                                                                        │
│//                                        ※変数"更新数"を経由しなくてもよい│
└──────────────────────────────────────┘
  一、ストアドプロシージャ内では、「COMMIT」しない
  二、ストアドプロシージャ内で、更新結果を出力する
    二の一、更新件数は更新直後に「sql%rowcount」を参照すればよい
    二の二、更新結果は「DBMS_OUTPUT.PUT_LINE()」で出力すればよい

で、実行する側ではこうなる。
┌──────────────────────────────────────┐
│SET SERVEROUTPUT ON;                                                        │
│EXECUTE "ストアドプロシージャ";                                             │
├──────────────────────────────────────┤
│COMMIT;                                                                     │
└──────────────────────────────────────┘
  一、最初に「SET SERVEROUTPUT ON」を実行し、
      SQL Developerに「DBMS_OUTPUT.PUT_LINE()」の結果が出力されるようにする
      ┌──────────────────────────────────┐
      │更新件数=1                                                         │
      │PL/SQLプロシージャが正常に完了しました。                            │
      └──────────────────────────────────┘
  二、更新件数の妥当性を確認した上で、「COMMIT」する
      ┌──────────────────────────────────┐
      │コミットが完了しました。                                            │
      └──────────────────────────────────┘
くれぐれも、「COMMIT」忘れのなきよう。
分類:Oracle
Oracle/ダブルクォーテーション
2017年10月05日
OracleのSQLでは表名や列名をダブルクォーテーションで囲っても
囲わなくてもよい。

但し、以下の場合は囲わなければならない。
・予約語を表名や列名として使用する場合
・大文字小文字を厳密に区別する場合

なお、囲わないと小文字は大文字に変換される。
なので、囲わないで上手くいっていた小文字を
敢えて囲ったことによりエラーとなるなんてこともあるから注意。
分類:Oracle
前へ 1 2 次へ