Oracle SQL 関数その1


ADD_MONTHS : 翌月日計算

 dateRet := ADD_MONTHS( dateSrc , N );
  
  ADD_MONTHSは日付dataSrcにNヵ月後の結果を返します。dataSrcの月数にNを加算した結果、日付が
  その月の最終日を越える場合は、日の部分はその月の最終日を返します。
SQL> SELECT SYSDATE,ADD_MONTHS(SYSDATE,1) AS ADD_1,
  2         ADD_MONTHS(SYSDATE,13) AS ADD_13,
  3         ADD_MONTHS(TO_DATE('2002/01/31'),1) AS ADD_N FROM DUAL;

SYSDATE  ADD_1    ADD_13   ADD_N
-------- -------- -------- --------
02-11-07 02-12-07 03-12-07 02-02-28

ADD_MONTHSの例としてADD_MONTHS(SYSDATE,13)はシステム日付の1年と1ヵ月後の日付を返します。
また、最後のADD_MONTHS(TO_DATE('2002/01/31'),1)は1ヶ月後の2月には31日は存在しない為、2月の最終日が結果として帰ります。

DECODE : コードデータ置換え

 DECODE( <式1>,<判定値1>,<結果値1>[,<判定値2>,<結果値2>, ... ] [ ,<デフォルト値> ] )

 DECODEは<式1>を<判定値1>から判定し等しい場合はその<結果値1>を返します。等しくない場合は順次
 <判定値2>,<判定値3>...と処理を進めます。全て等しくない場合はデフォルト値が設定されている場合
 そのデフォルト値を返します。全て一致しない場合はNULLを返します。
 このDECODEを利用することで1個のSQL文の中でIF~THEN~ELSEの処理を記述することができます。
SQL> SELECT DECODE(1,1,'CODE1',2,'CODE2',3,'CODE3','OTHER') AS DEC1,
  2         DECODE(3,1,'CODE1',2,'CODE2',3,'CODE3','OTHER') AS DEC3,
  3         DECODE(0,1,'CODE1',2,'CODE2',3,'CODE3','OTHER') AS DEC0,
  4         DECODE(0,1,'CODE1',2,'CODE2',3,'CODE3') AS DECN
  5  FROM DUAL;

DEC1  DEC3  DEC0  D
----- ----- ----- -
CODE1 CODE3 OTHER

4番目のDECODEは結果をNULLを返していますが、以下のSQLであればNULLであることがはっきりします。

SQL> SELECT NVL(DECODE(0,1,'CODE1',2,'CODE2',3,'CODE3'),'This is NULL.') FROM DUAL;

NVL(DECODE(0,
-------------
This is NULL.

その他のDECODEの便利な使い方としては、DECODEの結果とCOUNT,SUM等の関数を組み合わせるといろいろな
応用が利くと思います。以下に、EMP表を用いてDEPTNO毎の件数とSALの合計を計算しています。

SQL> SELECT SUM(DECODE(DEPTNO,10,1,0)),SUM(DECODE(DEPTNO,20,1,0)),SUM(DECODE(DEPTNO,30,1,0)),
  2         SUM(DECODE(DEPTNO,10,SAL,0)),SUM(DECODE(DEPTNO,20,SAL,0)),SUM(DECODE(DEPTNO,30,SAL,0))
  3  FROM EMP;

SUM(DECODE(DEPTNO,10,1,0)) SUM(DECODE(DEPTNO,20,1,0)) SUM(DECODE(DEPTNO,30,1,0))
-------------------------- -------------------------- --------------------------
SUM(DECODE(DEPTNO,10,SAL,0)) SUM(DECODE(DEPTNO,20,SAL,0)) SUM(DECODE(DEPTNO,30,SAL,0))
---------------------------- ---------------------------- ----------------------------
                         3                          5                          6
                        8750                        10875                         9400

上のSELECT文の件数を取得する部分を以下のSELECT文の様にCOUNT()関数を用いても可能です。
COUNTはNULL値を計数しないのでDECODEのデフォルト値をNULLにしています。
(COUNT,SUM,AVGのグループ関数はNULLを処理の対象としないことを利用しています)

SQL> SELECT COUNT(DECODE(DEPTNO,10,1,NULL)),COUNT(DECODE(DEPTNO,20,1,NULL)),COUNT(DECODE(DEPTNO,30,1,NULL))
  2  FROM EMP;

COUNT(DECODE(DEPTNO,10,1,NULL)) COUNT(DECODE(DEPTNO,20,1,NULL)) COUNT(DECODE(DEPTNO,30,1,NULL))
------------------------------- ------------------------------- -------------------------------
                              3                               5                               6

LAST_DAY : 指定月の最終日計算

 dateRet := LAST_DAY( dateSrc );
  
  LAST_DAYは日付dataSrcの末日の日付を返します。この関数を使えば簡単に該当月の最終日を計算してくれる
  ので一般の言語の様に別に関数を設ける必要がありません。
SQL> SELECT LAST_DAY(TO_DATE('2000/02/01')),LAST_DAY(TO_DATE('2001/02/01')),
  2         LAST_DAY(TO_DATE('2002/02/01')),LAST_DAY(TO_DATE('2003/02/01'))
  3  FROM DUAL;

LAST_DAY LAST_DAY LAST_DAY LAST_DAY
-------- -------- -------- --------
00-02-29 01-02-28 02-02-28 03-02-28

この例は2000年から2003年の2月の末日を求めています。閏年の計算をしなくても簡単に行えます。

SQL> SELECT
  2   LAST_DAY(SYSDATE) AS SYSDATE_LAST
  3  ,TO_CHAR(LAST_DAY(SYSDATE),'YYYY/MM/DD HH24:MI:SS') AS 日付文字列
  4  ,TO_CHAR(TRUNC(LAST_DAY(SYSDATE),'DD'),'YYYY/MM/DD HH24:MI:SS') AS 日付文字列2
  5  ,LAST_DAY(TO_DATE('2004/02/01')) FROM DUAL;

SYSDATE_ 日付文字列          日付文字列2         LAST_DAY
-------- ------------------- ------------------- --------
04-10-31 2004/10/31 17:53:25 2004/10/31 00:00:00 04-02-29

LAST_DAYは日付部分は月末を返すが、時刻部分は指定日付型の時刻をそのまま引き継ぐ様です。上のSQLを見ると良くわかるとは思いますが、もし時刻が00:00:00が必要であればTRUNC関数で切り捨てる必要があります。


EXTRACT : 日時フィールドを返す

 datetimeRet := EXTRACT( [YEAR,MONTH,DAY,HOUR,MINUTE,SECOND] FROM exp1 );

 EXTRACTはexp1の指定された日時フィールドを返します。
SQL> SELECT
  2   EXTRACT(YEAR  FROM TO_DATE('2004/10/20')) AS YEAR
  3  ,EXTRACT(MONTH FROM TO_DATE('2004/10/20')) AS MONTH
  4  ,EXTRACT(DAY   FROM TO_DATE('2004/10/20')) AS DAY
  5  ,TO_CHAR(SYSDATE,'YYYY/MM/DD HH24:MI:SS')  AS SYSTEM_DATE
  6  ,EXTRACT(HOUR   FROM TO_TIMESTAMP(TO_CHAR(SYSDATE,'YYYY/MM/DD HH24:MI:SS'))) AS HOUR
  7  ,EXTRACT(MINUTE FROM TO_TIMESTAMP(TO_CHAR(SYSDATE,'YYYY/MM/DD HH24:MI:SS'))) AS MINUTE
  8  ,EXTRACT(SECOND FROM TO_TIMESTAMP(TO_CHAR(SYSDATE,'YYYY/MM/DD HH24:MI:SS'))) AS SECOND
  9  FROM DUAL;

      YEAR      MONTH        DAY SYSTEM_DATE               HOUR     MINUTE     SECOND
---------- ---------- ---------- ------------------- ---------- ---------- ----------
      2004         10         20 2004/10/20 11:59:07         11         59          7

上記のSQLでは2~4行では年,月,日の値をそれぞれ返し、6~8行では時,分,秒を返します。6~8のTIMESTAP型データをDATE型に変更するとエラーが発生します。時,分,秒を取得するにはTIMESTAP型データでなければならない様です。

■MONTHS_BETWEEN : 月数の計算

 dateRet := MONTHS_BETWEEN( date1 , date2 );

 MONTHS_BETWEENはdate1とdate2の間の月数を返します。date1がdate2より大きい場合は正の値を、また
 date2がdate1より大きい場合は負の値を返します。同一日での比較の場合、及び月末同士の比較の場合は
  結果は整数の値になります。その他の場合は、1ヶ月を31日とした換算値が返されます。
SQL> SELECT
  2   MONTHS_BETWEEN(TO_DATE('2004/02/14'),TO_DATE('2004/01/14')) AS 月違いの同一日
  3  ,MONTHS_BETWEEN(TO_DATE('2004/01/14'),TO_DATE('2004/02/14')) AS 同上負値
  4  ,MONTHS_BETWEEN(TO_DATE('2004/01/14'),TO_DATE('2004/01/13')) AS 一日違い
  5  ,MONTHS_BETWEEN(TO_DATE('2004/02/29'),TO_DATE('2004/01/27')) AS 閏年1
  6  ,MONTHS_BETWEEN(TO_DATE('2004/02/29'),TO_DATE('2004/01/28')) AS 閏年2
  7  ,MONTHS_BETWEEN(TO_DATE('2004/02/29'),TO_DATE('2004/01/29')) AS 閏年3
  8  ,MONTHS_BETWEEN(TO_DATE('2004/02/29'),TO_DATE('2004/01/30')) AS 閏年4
  9  ,MONTHS_BETWEEN(TO_DATE('2004/02/29'),TO_DATE('2004/01/31')) AS 閏年5
 10  ,MONTHS_BETWEEN(TO_DATE('2004/11/30'),TO_DATE('2004/10/29')) AS T29
 11  ,MONTHS_BETWEEN(TO_DATE('2004/11/30'),TO_DATE('2004/10/30')) AS T30
 12  ,MONTHS_BETWEEN(TO_DATE('2004/11/30'),TO_DATE('2004/10/31')) AS T31
 13  FROM DUAL;

月違い同一日   同上負値   一日違い      閏年1      閏年2      閏年3      閏年4      閏年5        T29        T30        T31
------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
           1         -1 .032258065 1.06451613 1.03225806          1 .967741935          1 1.03225806          1          1

上記に、いろいろな場合のMONTHS_BETWEENの結果を示します。少し問題があるとすれば、閏年の場合には29日を1ヶ月として計算しているようである。但し、1月31日には月末の処理がなされて結果は1ヶ月の差として返されている。このあたりは、月数を処理する場合には注意が必要かもしれません。
尚、MONTHS_BETWEENの応用として、年齢の計算について以下に示します。

SQL> SELECT TRUNC(MONTHS_BETWEEN(SYSDATE,TO_DATE('1964/10/12'))/12) AS AGE FROM DUAL;

       AGE
----------
        40

REPLACE : 文字列置換

 charRet := REPLACE( charSrc , strSearch [ , strReplace ] );

  REPLACEは文字データcharSrcを先頭からサーチし、文字列strSearchと一致する部分を文字列strReplaceに
  置き換えます。strReplaceが指定されていない場合は置換えではなく一致する部分を削除します。
SQL> SELECT REPLACE('AABBBCCCCDDDDD','AA','XXXX'),REPLACE('AABBBCCCCDDDDD','AA') FROM DUAL;

REPLACE('AABBBCC REPLACE('AAB
---------------- ------------
XXXXBBBCCCCDDDDD BBBCCCCDDDDD

この例では最初は置換え処理を、2番目には削除処理を示しています。


TRANSLATE: 文字列変換

 charRet := TRANSLATE( charSrc , strSearch , strTranslate );

  TRANSLATEは文字データcharSrcを先頭からサーチし、文字列strSearchと一致する文字があった場合に
  変換文字列strTranslateの対応する文字に置換します。
  strTranslateがstrSearchより短い場合は、strSearchに対応する文字を削除します。
SQL> SELECT
  2   TRANSLATE('1234321', '123', 'ABC') AS TEST1 /* 1⇒A, 2⇒B, 3⇒C */
  3  ,TRANSLATE('1234321', '123', 'AB')  AS TEST2 /* 1⇒A, 2⇒B, 3⇒削除 */
  4  ,TRANSLATE('1234321', '12',  'ABC') AS TEST3 /* 1⇒A, 2⇒B*/
  5  ,TRANSLATE('あいうえお', 'あいう',  'AB') AS TEST4 /* あ⇒A, い⇒B, う⇒削除 */
  6  ,TRANSLATE('ABCDE', 'ABC',  'あい') AS TEST5 /* A⇒あ, B⇒い, C⇒削除 */
  7  FROM DUAL;

TEST1   TEST2 TEST3   TEST4  TEST5
------- ----- ------- ------ ------
ABC4CBA AB4BA AB343BA ABえお あいDE

TRANSLATEの注意点は、取扱が文字単位でありバイト単位ではないことです。上の例からも分かる様に、 シングルバイト文字とマルチバイト文字との変換が可能です。
但し、1文字単位での処理のため、「ダヂヅデド」などの半角の濁音文字を「ダヂヅデド」全角文字に変換するには 1回のTRANSLATEでは不可能ですので、PL/SQLで自分で関数を作るしかないかもしれません。


ROUND(数値) : 丸め処理

 numberRet := ROUND( numberSrc [ , numberUnit ] );

 ROUNDは数値データnumberSrcを小数点以下numberUnit桁までに丸めた数値を返します。
  numberUnitが省略されると0(ゼロ)と指定したことと同様で、小数点以下を丸めます。
  numberUnitが負の場合は、小数点から見てnumberUnit*(-1)の位置の桁を丸めます。
numberUnit値 丸める桁
3 小数点以下4桁目
2 小数点以下3桁目
1 小数点以下2桁目
0 小数点以下1桁目
-1 一の位の桁
-2 十の位の桁
-3 百の位の桁
SQL> SELECT ROUND(1234.5678, 3) DP4 ,ROUND(1234.5678, 2) DP3 ,
  2         ROUND(1234.5678, 1) DP2 ,ROUND(1234.5678, 0) DP1 ,
  3         ROUND(1234.5678,-1) DPL1,ROUND(1234.5678,-2) DPL2,ROUND(1234.5678,-3) DPL3
  4   FROM DUAL;

       DP4        DP3        DP2        DP1       DPL1       DPL2       DPL3
---------- ---------- ---------- ---------- ---------- ---------- ----------
  1234.568    1234.57     1234.6       1235       1230       1200       1000

ROUND(日付) : 丸め処理

 dateRet := ROUND( dateSrc [ , dateFormat ] );

 ROUNDは日付データdateSrcをdateFormatに従って丸めた日付を返します。
  dateFormatが省略されると最も近い日に丸められます。
dateFormat 丸め処理
YYYY,YEAR 年(7月1日で切り上げ)
Q 四半期
MONTH,MM 月(16日で切り上げ)
DD
DAY 週の開始日
HH,HH12,HH24
MI
SQL> SELECT ROUND(TO_DATE('2002/06/30'),'YEAR') YEAR0630,
  2         ROUND(TO_DATE('2002/07/01'),'YEAR') YEAR0701,
  3         ROUND(TO_DATE('2002/06/30'),'Q') Q,
  4         ROUND(TO_DATE('2002/06/15'),'MM')   MM0615  ,
  5         ROUND(TO_DATE('2002/06/16'),'MM') MM0616,
  6         ROUND(TO_DATE('2002/06/15 23:50:00','YYYY/MM/DD HH24:MI:SS'),'DD') DD,
  7         TO_CHAR(ROUND(TO_DATE('2002/06/15 23:29:00','YYYY/MM/DD HH24:MI:SS'),'HH24'),
  8                 'YYYY/MM/DD HH24:MI:SS') HH24
  9  FROM DUAL;

YEAR0630 YEAR0701 Q        MM0615   MM0616   DD       HH24
-------- -------- -------- -------- -------- -------- -------------------
02-01-01 03-01-01 02-07-01 02-06-01 02-07-01 02-06-16 2002/06/15 23:00:00


SIGN : 符号チェック

 numberRet := SIGN( numberSrc );

 SIGNは数値データnumberSrcが0(ゼロ)より小さい場合は-1を返し、numberSrcが0(ゼロ)の
  場合は0(ゼロ)を返し、numberSrcが0(ゼロ)より大きい場合は1を返します。
  numberSrcは数値を返す式を指定できます。
SQL> SELECT SIGN(-12),SIGN(0),SIGN(10) FROM DUAL;

 SIGN(-12)    SIGN(0)   SIGN(10)
---------- ---------- ----------
        -1          0          1

このSIGN関数と前述のDECODE関数を組み合わせることで、割と複雑なことが可能になります。
以下に簡単な例を示します。

SQL> SELECT EMPNO,SAL,DECODE(SIGN(SAL - 1500),-1,'1500未満',0,'1500同じ',1,'1500より大きい') FROM EMP;

     EMPNO        SAL DECODE(SIGN(SA
---------- ---------- --------------
      7369        800 1500未満
      7499       1600 1500より大きい
      7521       1250 1500未満
      7566       2975 1500より大きい
      7654       1250 1500未満
      7698       2850 1500より大きい
      7782       2450 1500より大きい
      7788       3000 1500より大きい
      7839       5000 1500より大きい
      7844       1500 1500同じ
      7876       1100 1500未満

     EMPNO        SAL DECODE(SIGN(SA
---------- ---------- --------------
      7900        950 1500未満
      7902       3000 1500より大きい
      7934       1300 1500未満

14行が選択されました。
転職を本気で考えている方向けのプログラミングスクール!【WebCampPRO】

SUBSTR : 文字列切出し

 stringRet := SUBSTR( stringSrc , numberPos [ , numberLen ] );

 SUBSTRは文字列stringSrcのnumberPos番目からnumberLen文字分の文字列を返します。
  (stringSrcの文字列の数え方は先頭が1として処理します。)
  numberPosが0(ゼロ)の場合は1として処理します。
 numberPosが正の数の場合はstringSrcの先頭から数え,numberPosが負の数の場合はstringSrcの
  最後から数えます。
 numberLenを指定しない場合は、stringSrcのnumberPos番目から最後までの文字列を返します。
SQL> SELECT SUBSTR('ABCDEFGHIJKLMNOPQRSTUVWXYZ',10,5),
  2         SUBSTR('ABCDEFGHIJKLMNOPQRSTUVWXYZ',10),
  3         SUBSTR('ABCDEFGHIJKLMNOPQRSTUVWXYZ',0,5),
  4         SUBSTR('ABCDEFGHIJKLMNOPQRSTUVWXYZ',-5,4)
  5  FROM DUAL;

SUBST SUBSTR('ABCDEFGHI SUBST SUBS
----- ----------------- ----- ----
JKLMN JKLMNOPQRSTUVWXYZ ABCDE VWXY

INSTR : 文字列検索

  numberRet := INSTR( stringSrc , stringChk , numberPos [ , numberOcr ] );

 INSTRは文字列stringSrcのnumberPos番目からstringChk文字列を検索します。
  numberOcr回数の検索ができた位置を結果として返します。
  (stringSrcの文字列の数え方は先頭が1として処理します。)
  numberPos及びnumberOcrのデフォルトは1として処理します。
 numberPosが正の数の場合はstringSrcの先頭から数え,numberPosが負の数の場合はstringSrcの
  最後から数えます。
SQL> SELECT INSTR('ABCDEFGHIJKLMNOPQRSTUVWXYZ','Z',1) AS 回数指定無し
  2        ,INSTR('ABCDEFGHIJKLMNOPQRSTUVWXYZ','A',-1) AS 回数指定無しで逆方向
  3        ,INSTR('ABCDEFGHIABCDEFGHIABCDEFGHI','A',1,3) AS 回数指定有り
  4        ,INSTR('ABCDEFGHIABCDEFGHIABCDEFGHI','A',2,2) AS 回数指定有りで途中から検索
  5  FROM DUAL;

回数指定無し 回数指定無しで逆方向 回数指定有り 回数指定有りで途中から検索
------------ -------------------- ------------ --------------------------
          26                    1           19                         19

実際のテーブルを使用した例を以下に示します。商品マスタを例に取りますが、商品名の中に”-”が
含まれていますが、それを検索して商品名の前半と後半に分ける処理をさせます。最初に商品マスタの一覧を示します。

SQL> select * from TM_商品;

商品コード 商品名                                   商品区分               仕入単価   売上単価
---------- ---------------------------------------- -------------------- ---------- ----------
         1 PC-9001                                  デスクトップパソコン      65000      98000
         2 PC-9002                                  デスクトップパソコン      95000     120000
         3 PC-9003                                  デスクトップパソコン     150000     190000
         4 NOTE-1010                                ノートパソコン           125000     188000
         5 NOTE-1020                                ノートパソコン           145000     200000
         6 NOTE-1030                                ノートパソコン           155000     220000
         7 PRT-3001                                 プリンタ                  45000      88000
         8 PRT-4001                                 プリンタ                 115000     180000
         9 CRT-1001                                 ディスプレイ              45000      78000
        10 CRT-2001                                 ディスプレイ              55000      98000
        11 HUB-A001                                 ネットワーク              20000      40000
        12 HUB-B001                                 ネットワーク              40000      60000
       100 TEST1000                                 デスクトップパソコン     100000     120000

13行が選択されました。

SUBSTR関数の引数の中に、INSTRの結果を渡しています。文字列の中を検索して、文字列の抽出や置換えにはこの様な方法が使えると思います。

SQL> SELECT '#' || SUBSTR(商品名,INSTR(商品名,'-') + 1) || '#' AS 商品名のハイフン以降
  2        ,'#' || SUBSTR(商品名,1,INSTR(商品名,'-')) || '#' AS 商品名のハイフン以前
  3  FROM TM_商品;

商品名のハイフン以降                     商品名のハイフン以前
---------------------------------------- ----------------------------------------
#9001#                                   #PC-#
#9002#                                   #PC-#
#9003#                                   #PC-#
#1010#                                   #NOTE-#
#1020#                                   #NOTE-#
#1030#                                   #NOTE-#
#3001#                                   #PRT-#
#4001#                                   #PRT-#
#1001#                                   #CRT-#
#2001#                                   #CRT-#
#A001#                                   #HUB-#
#B001#                                   #HUB-#
#TEST1000#                               ##

13行が選択されました。

実際のSQL/PLUSでこのSQLを実行すると、各カラムの長さが非常に長くなり各行が1行では表示できなくなります。この場合には、PLUSのコマンドのCOLUMNを使用して表示カラムの、表示形式を設定すれば1行で表示可能になります。

SQL> COLUMN 商品名のハイフン以降 FORMAT A40
SQL> COLUMN 商品名のハイフン以前 FORMAT A40

NVL , NVL2 : NULL文字列置換

 dataRet := NVL( dataSrc , dataChg );

 NVLはデータdataSrcがNULLの場合、dataChgを結果として返し、NULLで無い場合は
 dataSrcそのものを返します。 戻り値のデータ型はdataSrcのデータ型と同じになります。
SQL> SELECT NVL(NULL,'IS NULL') , NVL('123','IS NULL') FROM DUAL;

NVL(NUL NVL
------- ---
IS NULL 123
 dataRet := NVL2( dataSrc , dataNotNull, dataNull );

 NVL2はデータdataSrcがNULLの場合dataNullを結果として返し、NULL以外の場合dataNotNullを返します。
 dataNotNullと dataNullのデータ型が異なりdataNullがNULLで無い場合にはdataNullをdataNotNullの
 データ型に変換します。
 戻り値のデータ型はdataNotNullのデータ型と同じになります。
 dataNotNullが文字列型の場合はVRACHAR2型になります。
SQL> SELECT NVL2(NULL,'IS NOT NULL','IS NULL') , NVL2('123','IS NOT NULL','IS NULL') FROM DUAL;

NVL2(NU NVL2('123',
------- -----------
IS NULL IS NOT NULL

ページのトップへ戻る