0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

オラクルからMySQLへのSQL変換でのメモ

Last updated at Posted at 2023-10-01

はじめに

オラクルからmysqlへの書き換えてに、理解に苦しむsqlに出くわし、理解に時間がかかったので繰り返さないよう記録として残した。

該当SQL

以下オラクルsql、見にくいが整形でき、整形したものを見れば理解できるなら以降見る必要なし。

該当SQL1

SELECT
DECODE(SIGN(SUM(DECODE(SIGN(ABS(TAN*AMT - KIN)/
        DECODE(TAN*AMT,0,1,TAN*AMT)*100-6),1,1,0))),1,1,0) 
AS CHK_TAN
FROM
 sample

該当SQL2

select decode(nvl(max(txt_id),-1),-1,
((to_number(to_char(sysdate,'YYYY'))-".$year." + 
DECODE(SIGN(to_number(to_char(sysdate,'MM'))-
".$month."),'-1',1,0))*10000 + 1),max(txt_id)+1) from sample

1つ目(該当SQL1)

関数が理解できなければ整形もできない。
まず該当1に関して以下の関数がわかればいいと思う。

DECODE関数

DECODE(列名, 値1, 結果1, 値2, 結果2, 結果3)

引数1(列名)が値1だと結果1を返す、引数1(列名)が値2だと結果2を返す、それ以外は結果3を返す。

SIGN

SIGN関数は、引数で指定された数値の符号を求める関数。引数で指定された数値が正ならば「1」、負ならば「-1」、0ならば「0」が返される。

ABS

絶対値です。

読みやすく整形

該当SQLを読みやすく整形したものです。(あのままではわかるはずがない)

SELECT 
  DECODE(
    SIGN(
      SUM(
        DECODE(
          SIGN(
            ABS(TAN*AMT - KIN)/
            DECODE(TAN*AMT,0,1,TAN*AMT)*100-6
          ),
          1, 1, 0
        )
      )
    ),
    1, 1, 0
  ) AS CHK_TAN
FROM sample;

内側から内容を記載
1.SINGの中を計算
(1).TAN * AMT - KINの絶対値を計算する: ABS(TAN*AMT - KIN)
(2).TAN * AMT をチェックし、0の場合は1を、それ以外の場合は TAN*AMT の値を使用する。DECODE(TAN*AMT,0,1,TAN*AMT)
(3).ステップ1の値をステップ2の値で割る
(4).その結果に100を掛ける。
(5).そして、6を引く。
2.SIGN値
評価される値(SIGNの中)が正なら1,0なら0、負なら-1を返す。
3.DECODE
評価される値(SING値)が1なら1、それ以外なら0を返す。
4.SUM
これらの値(SIGN値)を、全行にわたって合計する。
5.SIGN
SUMで合計したものが、正なら1,0なら0、負なら-1を返す。
6.DECODE
SIGNの値が、1なら1を返す、それ以外は0を返す。

要は、要件を満たす行が1つでもあれば1、なければ0を返すsqlである。

MYSQL

オラクルのDECODE関数は、MySQLではCASE文を使用して書き直す。
SUM関数はテーブルでセレクトされる合計の集計関数である、いざこんな書き方をされるとわかりにくかった、SIGN関数に惑わされないこと。
該当SQLをMYSQLに書き換えた例。

SQL
CASE
    WHEN SIGN(SUM(CASE
                    WHEN SIGN(ABS(TAN * AMT - KIN) /
                        CASE
                            WHEN TAN * AMT = 0 THEN 1
                            ELSE TAN * AMT
                        END * 100 - 6) = 1 THEN 1
                    ELSE 0
                  END)) = 1 THEN 1
    ELSE 0
END AS CHK_TAN
FROM sample

上記sqlでわかりくい場合、さらに整形すると以下になり、長くなるがわかるはず。
とにかくただ階層が深い。

SELECT 
  (
    CASE 
      WHEN 
        SIGN(
          SUM(
            CASE 
              WHEN 
                SIGN(
                  ABS(TAN * AMT - KIN_D) / 
                  (CASE WHEN TAN * AMT = 0 THEN 1 ELSE TAN * AMT END) * 100 - 6
                ) = 1 
              THEN 1 
              ELSE 0 
            END
          )
        ) = 1 
      THEN 1 
      ELSE 0 
    END
  ) AS CHK_TANKA
FROM sample;

2つ目(該当SQL2)

select decode(nvl(max(txt_id),-1),-1,
((to_number(to_char(sysdate,'YYYY'))-".$year." + 
DECODE(SIGN(to_number(to_char(sysdate,'MM'))-
".$month."),'-1',1,0))*10000 + 1),max(txt_id)+1) from sample

該当1のsqlが理解でれば整形も可能かと思う。
整形すると以下になる。

SELECT DECODE(
  NVL(MAX(txt_id), -1),
  -1,
  (
    (
      TO_NUMBER(TO_CHAR(SYSDATE, 'YYYY')) - ".$year."
      + DECODE(
          SIGN(TO_NUMBER(TO_CHAR(SYSDATE, 'MM')) - ".$month."),
          '-1',
          1,
          0
        )
    ) * 10000 + 1
  ),
  MAX(ext_id) + 1
)
FROM sample;

関数の理解

以下のように分解
・NVL(MAX(ext_id), -1)
MAX(ext_id)は、ext_idというカラムの最大値を返す集約関数。

NVLは、NULL値を別の値に置き換える関数。この場合、NVL(MAX(ext_id), -1)は、MAX(ext_id)の結果がNULLであれば-1という値に置き換える。

・TO_NUMBER(TO_CHAR(SYSDATE, 'YYYY'))
SYSDATEは、現在のシステム日付と時刻を返す関数。
TO_CHARは、数値や日付型のデータを文字列に変換する関数。
つまり、TO_CHAR(SYSDATE, ‘YYYY’)は、SYSDATEの値を’YYYY’という書式で文字列に変換している。
TO_NUMBERは、文字列の数値への変換を行う関数。
つまりTO_NUMBER(TO_CHAR(SYSDATE, ‘YYYY’))は、TO_CHAR(SYSDATE, ‘YYYY’)の結果を数値に変換している。
つまり、TO_NUMBER(TO_CHAR(SYSDATE, ‘YYYY’))は、現在のシステム日付の年部分を数値として返す式である。

・DECODE、SIGNに関しては該当1参考

このSQL文の目的は、新しいtxt_idを生成すること。もしtxt_idが存在しない、またはその最大値が-1の場合、新しいtxt_idは特定の計算に基づいて生成される。

MySQLへの置き換え

DECODEやNVL関数は、同等の機能を提供する IFNULLやCASE などの関数を使用する。文字列、数値への変換対応は、特に必要なく、例えばYEAR(CURDATE())で数値となる。

SELECT 
  CASE
    WHEN IFNULL(MAX(ext_id), -1) = -1 THEN
      (
        (
          YEAR(CURDATE()) - ".$year."
          + CASE
              WHEN MONTH(CURDATE()) - ".$month." < 0 THEN 1
              ELSE 0
            END
        ) * 10000 + 1
      )
    ELSE MAX(ext_id) + 1
  END
FROM
 SAMPLE

おわりに

sql理解に苦しんだのだが、DECODE関数、SIGN関数が何かわからず、しかもネストが深いため思考が止まってしまったと思う。オラクルになれた人なら問題ないsqlかと思う。DECODE関数、SIGN関数がわかれば整形はできた。集計関数はテーブルを読む上で重要だが、なぜか本来の理解ができなかった。
個人的なブログになったかもしれないが、今後のsqlのステップとして残した。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?