【Oracle】PLSQLでBLOBデータを出力

PLSQLでBLOBデータを出力します。

CREATE OR REPLACE PROCEDURE testBlobOut (kanriNo IN VARHCAR) AS
CURSOR C_CONTROL IS
  SELECT
    TB1.IMAGE_NAME, 
    TB1.IMAGE
  FROM
    SAMPLE TB1
  WHERE
    TB1.KANRI_NO = kanriNo;

v_blob_locater BLOB;
v_offset INTEGER := 1;
v_buffer LONG RAW;
v_file_buffer_size INTEGER := 32000;
v_amount INTEGER := 32000;
v_totalsize INTEGER;
v_filetype UTL_FILE.FILE_TYPE;
v_filename VARCHAR2(1000) := '';
v_openmode VARHCAR2(2) := 'wb';
v_dir VARCHAR2(1000) := 'C/temp';

BEGIN
  OPEN C_CONTROL;
  LOOP
    FETCH C_CONTROL INTO v_blob_locater, v_filename;
    EXIT WHEN C_CONTROL%NOTFOUND;
    DBMS_OUTPUT.PUTLINE('fileName:' || v_filename);
    v_totalsize := DBMS_LOB.GETLENGTH(v_blob_locater);
    v_filetype := UTL_FILE.FOPEN(
      v_dir,
      v_filename,
      v_openmode,
      v_file_buffer_size
    );
    while v_offset < v_totalsize loop
      if v_offset + v_amount > v_totalsize then
        v_amount := v_totalsize - v_offset + 1;
      end if;
      DBMS_LOB.READ(
        v_blob_locater,
        v_amount,
        v_offset,
        v_buffer
      );
      UTL_FILE.PUT_RAW(
        v_filetype,
        v_buffer,
        true
      );
      v_offset := v_offset + v_amount;
      DBMS_OUTPUT.PUT_LINE('Offset :' || v_offset);
    end loop;
    UTL_FILE.FFLUSH(v_filetype);
    UTL_FILE.FCLOSE(v_filetype);

    DBMS_OUTPUT.PUT_LINE('fileSize :' || v_totalsize);
  END LOOP;
  CLOSE C_CONTROL;
EXCEPTION WHEN OTHERS THEN
  UTL_FILE.FCLOSE_ALL;
  RAISE;
END;
/

PLSQLの実行方法

set serveroutput ON
execute testBlobOut('000010');

【Oracle】バッチファイルからSQLPlusを起動しデータ取得

バッチファイルからSQLPlusを起動しデータ取得を行います。
主にエビデンス取得で利用します。
テスト対象の実施前後で取得し、結果ファイルを比較します。

  • 起動バッチ

    @echo off
    set DATE_TIME=%date:~0, 4%%date:~5, 2%%date:~2, 2%%time:~0, 2%%time:~3, 2%%time:~6, 2%
    
    set IP_ADDRESS=192.168.XX.XX
    set PORT_NO=1521
    set SID=XXX
    set USER_ID=XXX
    set PASSWORD=XXX
    set SQL_FILE=outputCsvAllData.sql
    set SPOOL_FILE=select_AllData.csv
    
    rem -s サイレントモード
    rem    プロンプトやログインメッセージを表示しなくなる。
    rem -L 1度だけログイン
    rem    ログインに失敗すると3回まで再試行するが、1回で終了する。
    sqlplus -s -L %USER_ID%/%PASSWORD%@%IP_ADDRESS%:%PORT_NO%/%SID% @%SQL_FILE%
    
    move %SPOOL_FILE% %DATE_TIME%_%SPOOL_FILE%
    
    echo %DATE_TIME%_%SPOOL_FILE%を出力しました。
    pause
    
  • SQLファイル(outputCsvAllData.sql)

    set echo off
    set linesize 9999
    set pagesize 0
    set trimspool on
    set feedback off
    
    spool select_AllData.csv
    @@select_AllData.sql
    spool off
    
  • 「@@」はパス名を含まないファイル名の場合、「呼び出し元のスクリプトのパス」を補完する。

    例)
    以下のフォルダ構成の場合、
    「@@」をつけると呼び出せるが、「@」の場合、エラーとなる。
    ・C:/起動バッチ
    ・C:/test/outputCsvAllData.sql
    ・C:/test/select_AllData.sql
    
  • SQLファイル(select_AllData.sql)

    prompt テーブル名
    SELECT 項目1 || 't' || 項目2 || 't' || 項目3 FROM テーブル名;
    
  • 文字列結合している理由は「【Oracle】SQLPlusで空白除去」を参照。

【Oracle】データファイルとコントロールファイルを利用したインポート

データファイルとコントロールファイルを利用して
Oracleにデータをインポートします。

  • バッチファイル
  • @echo off
    REM == バッチファイル起動ディレクトリ取得 ==
    SET BAT_PASS=%~dp0
    
    REM == データファイルパス設定 ==
    SET DATA_PASS=%BAT_PASS%\data\
    
    REM == コントロールファイルパス設定 ==
    SET CTL_PASS=%BAT_PASS%\ctl\
    
    REM == データファイル名 ==
    SET CSVEXT=.csv
    SET CTLEXT=.ctl
    SET LOGEXT=.log
    SET DATA_ANKEN_KANRI=t_anken_kanri
    
    echo ==============================
    echo 案件管理 データファイル取込
    echo ==============================
    sqlldr USERID='TESTUSR/PASSWORD@サービス名' CONTROL='%CTL_PASS%%DATA_ANKEN_KANRI%%CTLEXT%' DATA='%DATA_PASS%%DATA_ANKEN_KANRI%%CSVEXT%' LOG='%DATA_PASS%%DATA_ANKEN_KANRI%%LOGEXT%'
    
    pause
    
  • コントロールファイル
  • OPTIONS (ERRORS=0, ROWS=10000, DIRECT=TRUE)
    LOAD DATA
    INFILE './t_anken_kanri*.csv' "str '\r\n'"
    APPEND
    INTO TABLE "TESTUSR"."T_ANKEN_KANRI"
    FILEDS TERMINATED BY ','
    OPTIONALLY ENCLOSED BY '"' AND '"'
    DATE FORMAT "YYYY/MM/DD hh24:mi:ss"
    TRAILING NULLCOLS
    (
        項目名1
        項目名2
        項目名3
    )
    
  • データファイル
  • "aaaa","bbbb","cccc"
    

【Oracle】統計情報に関するSQL

統計情報に関するSQLのメモです。

  • テーブルの統計情報を取得

    EXEC DBMS_STATS.GATHER_TABLE_STATS('スキーマ名', 'テーブル名');
    
  • インデックスの統計情報を取得

    EXEC DBMS_STATS.GATHER_INDEX_STATS('スキーマ名', 'インデックス名');
    
  • テーブルの統計情報の状態確認

    SELECT UT.TABLE_NAME, UT.NUM_ROWS, UT.BLOCKS, UT.AVG_ROW_LEN, UT.SAMPLE_SIZE, UT.LAST_ANALYZED FROM USER_TABLES UT WHERE UT.TABLE_NAME NOT IN ('CHAINED_ROWS', 'CREATE$JAVA$LOB$TABLE', 'EXCEPTIONS', 'PLAN_TABLE', 'PROF$PLAN_TABLE') AND UT.TABLE_NAME NOT LIKE 'BIN$%' ORDER BY UT.TABLE_NAME; 
    
  • インデックスの統計情報の状態確認

    SELECT UI.TABLE_NAME, UI.INDEX_NAME, UI.NUM_ROWS, UI.SAMPLE_SIZE, UI.LAST_ANALYZED, UI.STATUS FROM USER_INDEXES UI WHERE UI.TABLE_NAME NOT IN ('CHAINED_ROWS', 'CREATE$JAVA$LOB$TABLE', 'EXCEPTIONS', 'PLAN_TABLE', 'PROF$PLAN_TABLE') AND UI.TABLE_NAME NOT LIKE 'BIN$%' ORDER BY UI.TABLE_NAME, UI.INDEX_NAME;
    

【Oracle】パスワードの有効期限

VMwareにOracle11gをインストールしているのですが、
久しぶりにsqlplusにログインしたところ以下のエラーが発生しました。

ERROR:
ORA-28002: the password will expire within 7 days

Oracle10gまではパスワードは無期限でしたが、
Oracle11gからはパスワードの有効期限があるようです。
パスワードの期限を無期限に変更し、
パスワードを再設定します。

sqlplusに管理者権限を持つユーザかsysユーザでログインします。

sqlplus sys/管理者パスワード as sysdba

以下のSQLを実行します。

alter profile default limit password_life_time unlimited;
alter user ユーザ名 identified by "パスワード";

パスワードはダブルクォーテーションで括らないとエラーになります。
記号の区別がつかないためと思われます。

【Oracle】これまでに実行したSQL一覧とCPU時間

これまでに実行したSQL一覧とCPU時間を統計データが格納されたビューから取得します。

SELECT SQL_TEXT, FETCHES, EXCUTIONS, LOADS, ELAPSED_TIME, CPU_TIME FROM V$SQL_STATS WHERE SQL_ID = 'xxxxxx';

SQL_TEXT:SQL文
FETCHES:フェッチを行った回数
LOADS:オブジェクトのロード回数
ELAPSED_TIME:SQL の実行に使用した累計の時間
CPU_TIME:SQL の実行に使用した累計の CPU 時間

【Oracle】1ヵ月間の範囲に含まれるにデータの抽出

業務では適用開始年月日(VALID_START_YMD)と
適用終了年月日(VALID_END_YMD)を持つテーブルが多いですが、
そこから指定の1ヵ月間の範囲に含まれるデータを抽出します。

WHERE VALID_START_YMD <= '月末' AND VALID_END_YMD >= '月初'
適用開始年月日にあてるのが「月末」
適用終了年月日にあてるのが「月初」

逆にしてしまうと1ヶ月間のうち、月初~15日、16日~月末のような
2レコードがある場合、取得できなくなります。

【Oracle】指定日から連続した日付を取得

SQLで指定日から連続した日付を取得します。
集計画面で日ごとの合計を表示したい場合の親テーブルとして利用できます。

  • 例1(ALL_CATALOGを利用)

    SELECT TO_DATE('20150101', 'YYYYMMDD') + ROWNUM -1 AS DCOL FROM ALL_CATALOG WHERE TO_DATE('20150101', 'YYYYMMDD') + ROWNUM -1 <= TO_DATE('20150131', 'YYYYMMDD')ORDER BY 1;
    

    FROM句には指定日から連続した日付の範囲分の行を持っている必要があります。
    今回の例では「ALL_CATALOG」を利用していますが、
    そもそも参照権限がない場合もあるので、1~31までのNoだけを持つ空テーブルを利用します。

  • 例2(ALL_CATALOGの代わりに1~31までのNoだけを持つ空テーブルを利用)

    SELECT TO_DATE('20150101', 'YYYYMMDD') + ROWNUM -1 AS DCOL FROM (SELECT LEVEL id FROM DUAL CONNECT BY LEBEL <= 31) WHERE TO_DATE('20150101', 'YYYYMMDD') + ROWNUM -1 <= TO_DATE('20150131', 'YYYYMMDD')ORDER BY 1;
    
  • 例3(1~31までのNoだけを持つ空テーブルだけを利用)

    SELECT SYSDATE + LEVEL AS HIZEKE FROM DUAL CONNECT BY LEBEL <= 31;
    

【Oracle】順位付け

SQLで点数の高い順位を採番します。
その際、同点の場合、1, 1, 3のように採番します。

SELECT RANK() OVER(ORDER BY 項目 DESC) FROM テーブル名

【Oracle】SQLで表記揺れ

住所の登録システムを作っているときにたまに要件で出てくる表記揺れ。
ひらがなは全て半角カタカナに変換したり、
括弧も全て()に変換したりします。

Oracle10gからの機能であるUTL_I18NパッケージのTRANSLITERATEファンクションを利用します。
下記に例を示しておきます。

テキトーな仕様
全角英数字:全て半角英数字に変換
全角ひらがな、カタカナ:全て半角カタカナに変換
前後の中間の空白:全角、半角問わず削除
全角半角の括弧{}、「」、<>、():全て()に変換
全角記号:半角記号に変換
全角半角のハイフン:全て-に変換
CREATE OR REPLACE FUNCTION CONVERSION_TEST(
    IN_CHAR VARCHAR
) RETURN VARCHAR
IS
BEGIN
RETURN
    UTL_I18N.TRANSLITERATE(
        TRANSLATE(
            UPPER(TO_SINGLE_BYTE(REPLACE(REPLACE(IN_CHAR,' '),' ')))
            ,'{}「」<>()ー-!#$%'
            ,'()()()()--!#$%'
            )
        ,'kana_hwkatakana')
    ;
END CONVERSION_TEST;

記号、括弧、ハイフンは全部書くとキリないので、省略しています。
「kana_hwkatakana」の部分は他にも色々あるようです。