Oracle Database
001. 実行計画の確認(その1)
「Enterprise Managerコンソール」→「SQLスクラッチパッド」

002. 実行計画の確認(その2)
--1 create table PLAN_TABLE(初回のみ) SQLPLUS @%ORACLE_HOME%\rdbms\admin\utlxplan.sql --2 表関数 VIEW 設定(参照し易くするための設定、初回のみ) CREATE VIEW plan AS SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('plan_table',NULL,'serial')); (%ORACLE_HOME%\rdbms\admin\utlxpls.sql) --3 SQL の前に EXPLAIN PLAN FOR を付ける EXPLAIN PLAN FOR SELECT * FROM ~; --4 実行計画の確認 SELECT * FROM plan; |
003. AUTOTRACE/実行計画とパフォーマンス統計情報
--1 ロールの作成(初回のみ、DBA 権限が必要) SQLPLUS @%ORACLE_HOME%\sqlplus\admin\plustrce.sql --2 権限の追加(必要に応じて) GRANT PLUSTRACE TO [ユーザ名] WITH ADMIN OPTION; --3 AUTOTRACE の開始 SET AUTOTRACE TRACEONLY; --4 AUTOTRACE の実行 SELECT * FROM ~; --5 AUTOTRACE の終了 SET AUTOTRACE OFF; /*--------------------------------------------------* * consistent gets : メモリへのアクセス数 * physical reads : 物理ディスクへのアクセス数 * sorts (memory) : メモリ内でのソート操作数 * sorts (disk) : 物理ディスク上でのソート操作数 *--------------------------------------------------*/ |
sqldeveloper > 権限設定を行うことで使えるようになる
SQL*Plusのチューニング
004. 一時表領域の再構築
--1 一時表領域(TEMP02.DBF)追加 ALTER TABLESPACE TEMP ADD TEMPFILE 'D:\ORACLE\ORADATA\ORCL\TEMP02.DBF' SIZE 10M AUTOEXTEND ON NEXT 640K; --2 TEMP01.DBF オフライン ALTER DATABASE TEMPFILE 'D:\ORACLE\ORADATA\ORCL\TEMP01.DBF' OFFLINE; --3 TEMP01.DBF 削除 ALTER DATABASE TEMPFILE 'D:\ORACLE\ORADATA\ORCL\TEMP01.DBF' DROP INCLUDING DATAFILES; --4 TEMP01.DBF 再作成 ALTER TABLESPACE TEMP ADD TEMPFILE 'D:\ORACLE\ORADATA\ORCL\TEMP01.DBF' SIZE 10M AUTOEXTEND ON NEXT 640K; --5 TEMP02.DBF オフライン ALTER DATABASE TEMPFILE 'D:\ORACLE\ORADATA\ORCL\TEMP02.DBF' OFFLINE; --6 TEMP02.DBF 削除 ALTER DATABASE TEMPFILE 'D:\ORACLE\ORADATA\ORCL\TEMP02.DBF' DROP INCLUDING DATAFILES; |
-- リサイズ ALTER DATABASE TEMPFILE 'D:\ORACLE\ORADATA\ORCL\TEMP01.DBF' RESIZE 10M; |
005. よく使う動的パフォーマンス(V$)・ビュー
-- セッション情報 SELECT * FROM V$SESSION; -- 実行中のトランザクション SELECT * FROM V$TRANSACTION; -- ロック情報 SELECT * FROM V$LOCK; -- 予約語 SELECT * FROM V$RESERVED_WORDS; -- SQL文のテキスト SELECT * FROM V$SQLTEXT; SELECT * FROM V$SQLTEXT_WITH_NEWLINES; -- 実行計画情報 SELECT * FROM V$SQL_PLAN; -- ライセンス制限の情報 SELECT * FROM V$LICENSE; -- NLSパラメータの現在の設定値 SELECT * FROM V$NLS_PARAMETERS; -- NLSパラメータのすべての有効値 SELECT * FROM V$NLS_VALID_VALUES; |

006. 階層問合せ (Hierarchical Query)
▽親(情報システム本部)から子孫を検索
SELECT LEVEL , CONNECT_BY_ISLEAF -- リーフ(子)がいない場合に 1 を戻す , CONNECT_BY_ROOT 組織名 , PRIOR AS 組織名 , LPAD(' ',LEVEL*2-2) || 組織名 , SYS_CONNECT_BY_PATH(組織名,'/') , T1.* FROM 組織マスタ T1 START WITH 組織名 = '情報システム本部' CONNECT BY PRIOR 子コード = 親コード |
▽子(情報システム本部)から親を検索
SELECT LEVEL , CONNECT_BY_ISLEAF , CONNECT_BY_ISCYCLE -- これ以上検索できない場合に 1 を戻す(NOCYCLE を指定すると使える) , CONNECT_BY_ROOT 組織名 , PRIOR 組織名 , LPAD(' ',LEVEL*2-2) || 組織名 , SYS_CONNECT_BY_PATH(組織名,'/') , T1.* FROM 組織マスタ START WITH 組織名 = '情報システム本部' CONNECT BY NOCYCLE PRIOR 親コード = 子コード |
▽親(情報システム本部)の子だけを検索
SELECT 組織名 FROM 組織マスタ WHERE PRIOR 子コード = 親コード START WITH 組織名 = '情報システム本部' CONNECT BY PRIOR 子コード = 親コード |

007. RANK/ランク
▽購入履歴テーブルから顧客コードごとに最近の購入日を取得する
SELECT * FROM ( SELECT RANK() OVER(PARTITION BY 顧客コード ORDER BY 購入日 DESC) v_rank , T1.* FROM 購入履歴テーブル T1 WHERE 購入日 <= SYSDATE ) WHERE v_rank = 1 |
▽RANK を使わない方法
SELECT * FROM 購入履歴テーブル WHERE (顧客コード, 購入日) IN ( SELECT 顧客コード, MAX(購入日) FROM 購入履歴テーブル WHERE 購入日 <= SYSDATE GROUP BY 顧客コード ) |
※Teradata だと Qualify 句があるのでもう少し便利です。
008. テーブル定義
SELECT T1.table_name , T1.column_id , T1.column_name , T1.data_type , NVL(T1.data_precision, T1.char_col_decl_length) AS data_length , T1.data_scale , T1.nullable , T2.comments FROM user_tab_columns T1 LEFT OUTER JOIN user_col_comments T2 ON T1.table_name = T2.table_name AND T1.column_name = T2.column_name WHERE T1.table_name = 'テーブル名' ORDER BY 1,2 |
009. インデックス定義
SELECT * FROM user_ind_columns WHERE table_name = 'テーブル名' ORDER BY table_name, index_name, column_position |
010. 数値切り上げ/ROUNDUP
-- /********************************************************************************************** * Function Name : ROUNDUP * Description : 数値切り上げ(小数点第?位を切り上げ) * Argument : i_number IN NUMBER 数値 * : i_scale IN NUMBER 小数点の桁数 **********************************************************************************************/ -- CREATE OR REPLACE FUNCTION roundup (i_number IN NUMBER, i_scale IN NUMBER) RETURN NUMBER IS ret_value NUMBER; -- 戻り値 v_sign NUMBER := +1; -- i_number が正:+1、負:-1 BEGIN -- プラス/マイナスの符号 IF i_number < 0 THEN v_sign := -1; END IF; -- 絶対値を取る ret_value := ABS(i_number); -- 切り上げ処理 ret_value := TRUNC(ret_value + POWER(0.1, i_scale + 1) * 9, i_scale); -- 絶対値を戻す ret_value := ret_value * v_sign; RETURN ret_value; EXCEPTION WHEN OTHERS THEN RETURN 0; END roundup; / SHOW ERRORS |
011. 横持ち→縦持ち (UNPIVOT)
▽横持ち→縦持ち(その1)
WITH v_tab1 AS ( SELECT 'amt' AS account, 40 AS apr, 50 AS may, NULL AS jun, 70 AS jul, 80 AS aug, 90 AS sep FROM dual UNION ALL SELECT 'qty' AS account, 41 AS apr, 51 AS may, 61 AS jun, 71 AS jul, 81 AS aug, 91 AS sep FROM dual ) SELECT account, 'apr', apr FROM v_tab1 UNION ALL SELECT account, 'may', may FROM v_tab1 UNION ALL SELECT account, 'jun', jun FROM v_tab1 UNION ALL SELECT account, 'jul', jul FROM v_tab1 UNION ALL SELECT account, 'aug', aug FROM v_tab1 UNION ALL SELECT account, 'sep', sep FROM v_tab1 ; |
▽横持ち→縦持ち(その2)
WITH v_tab1 AS ( SELECT 'amt' AS account, 40 AS apr, 50 AS may, NULL AS jun, 70 AS jul, 80 AS aug, 90 AS sep FROM dual UNION ALL SELECT 'qty' AS account, 41 AS apr, 51 AS may, 61 AS jun, 71 AS jul, 81 AS aug, 91 AS sep FROM dual ), v_col1 AS ( SELECT 'apr' AS period FROM dual UNION ALL SELECT 'may' FROM dual UNION ALL SELECT 'jun' FROM dual UNION ALL SELECT 'jul' FROM dual UNION ALL SELECT 'aug' FROM dual UNION ALL SELECT 'sep' FROM dual ) SELECT T1.account , V1.period , CASE WHEN V1.period = 'apr' THEN T1.apr ELSE 0 END + CASE WHEN V1.period = 'may' THEN T1.may ELSE 0 END + CASE WHEN V1.period = 'jun' THEN T1.jun ELSE 0 END + CASE WHEN V1.period = 'jul' THEN T1.jul ELSE 0 END + CASE WHEN V1.period = 'aug' THEN T1.aug ELSE 0 END + CASE WHEN V1.period = 'sep' THEN T1.sep ELSE 0 END AS num FROM v_tab1 T1 , v_col1 V1 ; |
▽横持ち→縦持ち(その3) [11g]
WITH v_tab1 AS ( SELECT 'amt' AS account, 40 AS apr, 50 AS may, NULL AS jun, 70 AS jul, 80 AS aug, 90 AS sep FROM dual UNION ALL SELECT 'qty' AS account, 41 AS apr, 51 AS may, 61 AS jun, 71 AS jul, 81 AS aug, 91 AS sep FROM dual ) SELECT * FROM v_tab1 UNPIVOT INCLUDE NULLS (num FOR period IN (apr, may, jun, jul, aug, sep)) ; |
012. 縦持ち→横持ち (PIVOT)
▽縦持ち→横持ち(その1)
WITH v_tab2 AS ( SELECT 'amt' AS account, 'apr' AS period, 40 AS num FROM dual UNION ALL SELECT 'amt' , 'may' , 50 FROM dual UNION ALL SELECT 'amt' , 'jun' , NULL FROM dual UNION ALL SELECT 'amt' , 'jul' , 70 FROM dual UNION ALL SELECT 'amt' , 'aug' , 80 FROM dual UNION ALL SELECT 'amt' , 'sep' , 90 FROM dual UNION ALL SELECT 'qty' , 'apr' , 41 FROM dual UNION ALL SELECT 'qty' , 'may' , 51 FROM dual UNION ALL SELECT 'qty' , 'jun' , 61 FROM dual UNION ALL SELECT 'qty' , 'jul' , 71 FROM dual UNION ALL SELECT 'qty' , 'aug' , 81 FROM dual UNION ALL SELECT 'qty' , 'sep' , 91 FROM dual ) SELECT account , SUM(CASE WHEN period = 'apr' THEN num ELSE 0 END) AS apr , SUM(CASE WHEN period = 'may' THEN num ELSE 0 END) AS may , SUM(CASE WHEN period = 'jun' THEN num ELSE 0 END) AS jun , SUM(CASE WHEN period = 'jul' THEN num ELSE 0 END) AS jul , SUM(CASE WHEN period = 'aug' THEN num ELSE 0 END) AS aug , SUM(CASE WHEN period = 'sep' THEN num ELSE 0 END) AS sep FROM v_tab2 GROUP BY account ; |
▽縦持ち→横持ち(その2) [11g]
WITH v_tab2 AS ( SELECT 'amt' AS account, 'apr' AS period, 40 AS num FROM dual UNION ALL SELECT 'amt' , 'may' , 50 FROM dual UNION ALL SELECT 'amt' , 'jun' , NULL FROM dual UNION ALL SELECT 'amt' , 'jul' , 70 FROM dual UNION ALL SELECT 'amt' , 'aug' , 80 FROM dual UNION ALL SELECT 'amt' , 'sep' , 90 FROM dual UNION ALL SELECT 'qty' , 'apr' , 41 FROM dual UNION ALL SELECT 'qty' , 'may' , 51 FROM dual UNION ALL SELECT 'qty' , 'jun' , 61 FROM dual UNION ALL SELECT 'qty' , 'jul' , 71 FROM dual UNION ALL SELECT 'qty' , 'aug' , 81 FROM dual UNION ALL SELECT 'qty' , 'sep' , 91 FROM dual ) SELECT account , apr , may , jun , jul , aug , sep FROM v_tab2 PIVOT (SUM(num) FOR period IN ('apr' AS apr, 'may' AS may, 'jun' AS jun, 'jul' AS jul, 'aug' AS aug, 'sep' AS sep)) ; |
▽縦持ち→横持ちの考え方(横持ち→縦持ちはこの逆)
1.元データ
account | period | num |
---|---|---|
amt | apr | 40 |
amt | may | 50 |
amt | jun | |
amt | jul | 70 |
amt | aug | 80 |
amt | sep | 90 |
qty | apr | 41 |
qty | may | 51 |
qty | jun | 61 |
qty | jul | 71 |
qty | aug | 81 |
qty | sep | 91 |
2.データを斜めに配置する
apr | may | jun | jul | aug | sep | |
---|---|---|---|---|---|---|
amt | 40 | |||||
amt | 50 | |||||
amt | 0 | |||||
amt | 70 | |||||
amt | 80 | |||||
amt | 90 | |||||
qty | 41 | |||||
qty | 51 | |||||
qty | 61 | |||||
qty | 71 | |||||
qty | 81 | |||||
qty | 91 |
3.ぎゅっと縦に圧縮 (GROUP BY) する
apr | may | jun | jul | aug | sep | |
---|---|---|---|---|---|---|
amt | 40 | 50 | 0 | 70 | 80 | 90 |
qty | 41 | 51 | 61 | 71 | 81 | 91 |
013. YYYYMM ⇔ FY, Mon 変換
ALTER SESSION SET NLS_DATE_LANGUAGE = 'AMERICAN'; -- YYYYMM を FY, Mon に変換 SELECT year_month , 'FY' || SUBSTR(year_month - 4,3,2) AS fiscal_year , TO_CHAR(TO_DATE(year_month,'YYYYMM'),'Mon','NLS_DATE_LANGUAGE=AMERICAN') AS period FROM ( SELECT '200901' AS year_month FROM dual UNION SELECT '200902' AS year_month FROM dual UNION SELECT '200903' AS year_month FROM dual UNION SELECT '200904' AS year_month FROM dual UNION SELECT '200905' AS year_month FROM dual UNION SELECT '200906' AS year_month FROM dual UNION SELECT '200907' AS year_month FROM dual UNION SELECT '200908' AS year_month FROM dual UNION SELECT '200909' AS year_month FROM dual UNION SELECT '200910' AS year_month FROM dual UNION SELECT '200911' AS year_month FROM dual UNION SELECT '200912' AS year_month FROM dual ) ORDER BY 1 ; -- FY, Mon を YYYYMM に変換 SELECT fiscal_year , period , TO_CHAR( TO_DATE( SUBSTR(fiscal_year,3,2) + CASE WHEN period IN ('Jan','Feb','Mar') THEN 1 ELSE 0 END || period , 'YYMon' , 'NLS_DATE_LANGUAGE=AMERICAN' ) , 'YYYYMM' ) AS year_month FROM ( SELECT 'FY08' AS fiscal_year, 'Jan' AS period FROM dual UNION SELECT 'FY08' AS fiscal_year, 'Feb' AS period FROM dual UNION SELECT 'FY08' AS fiscal_year, 'Mar' AS period FROM dual UNION SELECT 'FY09' AS fiscal_year, 'Apr' AS period FROM dual UNION SELECT 'FY09' AS fiscal_year, 'May' AS period FROM dual UNION SELECT 'FY09' AS fiscal_year, 'Jun' AS period FROM dual UNION SELECT 'FY09' AS fiscal_year, 'Jul' AS period FROM dual UNION SELECT 'FY09' AS fiscal_year, 'Aug' AS period FROM dual UNION SELECT 'FY09' AS fiscal_year, 'Sep' AS period FROM dual UNION SELECT 'FY09' AS fiscal_year, 'Oct' AS period FROM dual UNION SELECT 'FY09' AS fiscal_year, 'Nov' AS period FROM dual UNION SELECT 'FY09' AS fiscal_year, 'Dec' AS period FROM dual ) ORDER BY 3 ; |
014. SGA設定変更
SGA(システム・グローバル・エリア)
-- 共有プールのサイズを大きくする ALTER SYSTEM SET SHARED_POOL_SIZE = 256M; -- データベース・キャッシュ・サイズを大きくする ALTER SYSTEM SET DB_CACHE_SIZE = 256M; -- パラメータを表示 SHOW PARAMETERS SHARED_POOL_SIZE; SHOW PARAMETERS DB_CACHE_SIZE; SHOW PARAMETERS; |
015. 正規表現(数字か文字かの判断)
SELECT CASE WHEN REGEXP_LIKE('AB3', '^-?[0-9]{0,11}\.?[0-9]{0,2}$') THEN '数字' ELSE '文字' END , CASE WHEN REGEXP_LIKE('123', '^-?[0-9]{0,11}\.?[0-9]{0,2}$') THEN '数字' ELSE '文字' END , CASE WHEN REGEXP_LIKE('123.45', '^-?[0-9]{0,11}\.?[0-9]{0,2}$') THEN '数字' ELSE '文字' END , CASE WHEN REGEXP_LIKE('-123.45', '^-?[0-9]{0,11}\.?[0-9]{0,2}$') THEN '数字' ELSE '文字' END , CASE WHEN REGEXP_LIKE('123.456', '^-?[0-9]{0,11}\.?[0-9]{0,2}$') THEN '数字' ELSE '文字' END , CASE WHEN REGEXP_LIKE(' 123 ', '^-?[0-9]{0,11}\.?[0-9]{0,2}$') THEN '数字' ELSE '文字' END , CASE WHEN REGEXP_LIKE('', '^-?[0-9]{0,11}\.?[0-9]{0,2}$') THEN '数字' ELSE '文字' END , CASE WHEN REGEXP_LIKE('-123.45', '^-') THEN '負' ELSE '正' END FROM dual ; |
016. UPDATE(その1)
▽組織マスタ
組織コード(PK) | 組織名称 |
---|---|
01 | ABC株式会社 |
0101 | 情報システム本部 |
010101 | アプリケーション開発部 |
UPDATE 組織マスタ SET 組織名称 = 'システム開発部' WHERE 組織コード = '010101' ; |
▽組織マスタ (UPDATE 結果)
組織コード(PK) | 組織名称 |
---|---|
01 | ABC株式会社 |
0101 | 情報システム本部 |
010101 | システム開発部 |
017. UPDATE(その2)
▽社員マスタ
社員番号(PK) | 氏名 | 組織コード | 組織名称 |
---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 |
10002 | 前 大峰 | 0101 | 情報システム本部 |
10003 | 酒井田 柿右衛門 | 010101 | アプリケーション開発部 |
10004 | 今泉 今右衛門 | 010101 | アプリケーション開発部 |
10005 | 牧谿 | 010101 | アプリケーション開発部 |
▽組織マスタ
組織コード(PK) | 組織名称 |
---|---|
01 | ABC株式会社 |
0101 | 情報システム本部 |
010101 | システム開発部 |
UPDATE 社員マスタ T0 SET T0.組織名称 = ( SELECT M1.組織名称 FROM 組織マスタ M1 WHERE M1.組織コード = T0.組織コード ) ; |
▽社員マスタ (UPDATE 結果)
社員番号(PK) | 氏名 | 組織コード | 組織名称 |
---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 |
10002 | 前 大峰 | 0101 | 情報システム本部 |
10003 | 酒井田 柿右衛門 | 010101 | システム開発部 |
10004 | 今泉 今右衛門 | 010101 | システム開発部 |
10005 | 牧谿 | 010101 | システム開発部 |
018. UPDATE(その3)
▽社員マスタ
社員番号(PK) | 氏名 | 組織コード | 組織名称 |
---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 |
10002 | 前 大峰 | 0101 | 情報システム本部 |
10003 | 酒井田 柿右衛門 | 010101 | システム開発部 |
10004 | 今泉 今右衛門 | 010101 | システム開発部 |
10005 | 牧谿 | 010101 | システム開発部 |
10006 | 関羽 雲長 | NEW |
▽新組織マスタ
組織コード(PK) | 旧組織コード | 組織名称 |
---|---|---|
01 | 01 | ABC株式会社 |
0101 | NEW | 人事・総務本部 |
0102 | 0101 | 情報システム本部 |
010201 | 010101 | 第一システム開発部 |
UPDATE 社員マスタ T0 SET (組織コード, 組織名称) = ( SELECT M2.組織コード, M2.組織名称 FROM 新組織マスタ M2 WHERE M2.旧組織コード = T0.組織コード ) WHERE EXISTS ( SELECT NULL FROM 新組織マスタ M2 WHERE M2.旧組織コード = T0.組織コード AND M2.組織コード <> M2.旧組織コード ); |
▽社員マスタ (UPDATE 結果)
社員番号(PK) | 氏名 | 組織コード | 組織名称 |
---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 |
10002 | 前 大峰 | 0102 | 情報システム本部 |
10003 | 酒井田 柿右衛門 | 010201 | 第一システム開発部 |
10004 | 今泉 今右衛門 | 010201 | 第一システム開発部 |
10005 | 牧谿 | 010201 | 第一システム開発部 |
10006 | 関羽 雲長 | 0101 | 人事・総務本部 |
019. UPDATE(その4)
▽社員マスタ
社員番号(PK) | 氏名 | 組織コード | 組織名称 |
---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 |
10002 | 前 大峰 | 0102 | 情報システム本部 |
10003 | 酒井田 柿右衛門 | 010201 | 第一システム開発部 |
10004 | 今泉 今右衛門 | 010201 | 第一システム開発部 |
10005 | 牧谿 | 010201 | 第一システム開発部 |
10006 | 関羽 雲長 | 0101 | 人事・総務本部 |
UPDATE ( SELECT 社員番号 , 組織コード , 組織名称 FROM 社員マスタ WHERE 組織名称 = '第一システム開発部' ) SET 組織コード = '01020A' , 組織名称 = '第二システム開発部' ; |
▽社員マスタ (UPDATE 結果)
社員番号(PK) | 氏名 | 組織コード | 組織名称 |
---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 |
10002 | 前 大峰 | 0102 | 情報システム本部 |
10003 | 酒井田 柿右衛門 | 01020A | 第二システム開発部 |
10004 | 今泉 今右衛門 | 01020A | 第二システム開発部 |
10005 | 牧谿 | 01020A | 第二システム開発部 |
10006 | 関羽 雲長 | 0101 | 人事・総務本部 |
020. UPDATE(その5)
▽社員マスタ
社員番号(PK) | 氏名 | 組織コード | 組織名称 |
---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 |
10002 | 前 大峰 | 0102 | 情報システム本部 |
10003 | 酒井田 柿右衛門 | 01020A | 第二システム開発部 |
10004 | 今泉 今右衛門 | 01020A | 第二システム開発部 |
10005 | 牧谿 | 01020A | 第二システム開発部 |
10006 | 関羽 雲長 | 0101 | 人事・総務本部 |
▽新組織マスタ
組織コード(PK) | 旧組織コード | 組織名称 |
---|---|---|
01 | 01 | ABC株式会社 |
0101 | NEW | 人事・総務本部 |
0102 | 0101 | 情報システム本部 |
010201 | 010101 | 第一システム開発部 |
↓組織コード(PK)が結合しないところは NULL となる
UPDATE ( SELECT T0.社員番号 , T0.氏名 , T0.組織コード , T0.組織名称 , M2.組織コード AS UP_組織コード , M2.組織名称 AS UP_組織名称 FROM 社員マスタ T0 LEFT OUTER JOIN 新組織マスタ M2 ON T0.組織コード = M2.組織コード ) SET 組織コード = UP_組織コード , 組織名称 = UP_組織名称 ; |
▽社員マスタ (UPDATE 結果)
社員番号(PK) | 氏名 | 組織コード | 組織名称 |
---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 |
10002 | 前 大峰 | 0102 | 情報システム本部 |
10003 | 酒井田 柿右衛門 | ||
10004 | 今泉 今右衛門 | ||
10005 | 牧谿 | ||
10006 | 関羽 雲長 | 0101 | 人事・総務本部 |
↓キーで結合していないため、エラーが発生する。
UPDATE ( SELECT T0.社員番号 , T0.氏名 , T0.組織コード , T0.組織名称 , M2.組織コード AS UP_組織コード , M2.組織名称 AS UP_組織名称 FROM 社員マスタ T0 LEFT OUTER JOIN 新組織マスタ M2 ON T0.組織コード = M2.組織コード OR (REGEXP_INSTR(T0.組織名称,'システム開発部$') > 0 AND REGEXP_INSTR(M2.組織名称,'システム開発部$') > 0) ) SET 組織コード = UP_組織コード , 組織名称 = UP_組織名称 ; |
SQLエラー: ORA-01779: キー保存されていない表にマップする列は変更できません
↓キーで結合していないが、ヒント(BYPASS_UJVC)によりエラーを回避できる。
SELECT 結果の内容をよく確認することが大事。
基本は、更新するテーブルのキーが SELECT 内でユニークになっていること。ユニークでない場合、UPDATE が複数回実行される。
万一、UPDATE が複数回実行されてしまう場合でも、更新内容(UP_組織コード, UP_組織名称)が
社員番号(PK)に対してユニークであること。
社員番号(PK)に対して更新内容がユニークでないと、更新内容が保障されない。
※ORACLE のドキュメント「SQLリファレンス」等にヒント(BYPASS_UJVC)は掲載されていないため、使用には注意が必要。
UPDATE ( SELECT /*+ BYPASS_UJVC */ T0.社員番号 , T0.氏名 , T0.組織コード , T0.組織名称 , M2.組織コード AS UP_組織コード , M2.組織名称 AS UP_組織名称 FROM 社員マスタ T0 LEFT OUTER JOIN 新組織マスタ M2 ON T0.組織コード = M2.組織コード OR (REGEXP_INSTR(T0.組織名称,'システム開発部$') > 0 AND REGEXP_INSTR(M2.組織名称,'システム開発部$') > 0) ) SET 組織コード = UP_組織コード , 組織名称 = UP_組織名称 ; |
▽社員マスタ (UPDATE 結果)
社員番号(PK) | 氏名 | 組織コード | 組織名称 |
---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 |
10002 | 前 大峰 | 0102 | 情報システム本部 |
10003 | 酒井田 柿右衛門 | 010201 | 第一システム開発部 |
10004 | 今泉 今右衛門 | 010201 | 第一システム開発部 |
10005 | 牧谿 | 010201 | 第一システム開発部 |
10006 | 関羽 雲長 | 0101 | 人事・総務本部 |
↓MERGE ではヒント(BYPASS_UJVC)を使わなくてもエラーが発生しない。
やっていることは UPDATE /*+ BYPASS_UJVC */ と同じ。UPDATE が複数回実行されないように SELECT 結果の内容をよく確認することが大事。
MERGE INTO 社員マスタ T0 USING ( SELECT T1.社員番号 , T1.氏名 , T1.組織コード , T1.組織名称 , M2.組織コード AS UP_組織コード , M2.組織名称 AS UP_組織名称 FROM 社員マスタ T1 LEFT OUTER JOIN 新組織マスタ M2 ON T1.組織コード = M2.組織コード OR SUBSTR(T1.組織名称,3) = SUBSTR(M2.組織名称,3) ) T1 ON (T0.社員番号 = T1.社員番号) WHEN MATCHED THEN UPDATE SET 組織コード = T1.UP_組織コード , 組織名称 = T1.UP_組織名称 ; |
▽社員マスタ (UPDATE 結果)
社員番号(PK) | 氏名 | 組織コード | 組織名称 |
---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 |
10002 | 前 大峰 | 0102 | 情報システム本部 |
10003 | 酒井田 柿右衛門 | 010201 | 第一システム開発部 |
10004 | 今泉 今右衛門 | 010201 | 第一システム開発部 |
10005 | 牧谿 | 010201 | 第一システム開発部 |
10006 | 関羽 雲長 | 0101 | 人事・総務本部 |
021. 集計関数の結果で UPDATE → MERGE
▽社員マスタ
社員番号(PK) | 氏名 | 組織コード | 組織名称 | 同位組織人数 |
---|---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 | |
10002 | 前 大峰 | 0102 | 情報システム本部 | |
10003 | 酒井田 柿右衛門 | 010201 | 第一システム開発部 | |
10004 | 今泉 今右衛門 | 010201 | 第一システム開発部 | |
10005 | 牧谿 | 010201 | 第一システム開発部 | |
10006 | 関羽 雲長 | 0101 | 人事・総務本部 |
↓集計関数の結果で UPDATE しているためエラーが発生する
UPDATE ( SELECT 社員番号 , 氏名 , 組織コード , 組織名称 , 同位組織人数 , COUNT(*) OVER(PARTITION BY 組織コード) AS UP_同位組織人数 FROM 社員マスタ ) SET 同位組織人数 = UP_同位組織人数 ; |
SQLエラー: ORA-01732: このビューではデータ操作が無効です
↓集計関数の結果で UPDATE するには MERGE を使う
MERGE INTO 社員マスタ T0 USING ( SELECT 社員番号 , 氏名 , 組織コード , 組織名称 , 同位組織人数 , COUNT(*) OVER(PARTITION BY 組織コード) AS UP_同位組織人数 FROM 社員マスタ ) T1 ON (T0.社員番号 = T1.社員番号) WHEN MATCHED THEN UPDATE SET 同位組織人数 = T1.UP_同位組織人数 ; |
▽社員マスタ (UPDATE 結果)
社員番号(PK) | 氏名 | 組織コード | 組織名称 | 同位組織人数 |
---|---|---|---|---|
10001 | 俵屋 宗達 | 01 | ABC株式会社 | 1 |
10002 | 前 大峰 | 0102 | 情報システム本部 | 1 |
10003 | 酒井田 柿右衛門 | 010201 | 第一システム開発部 | 3 |
10004 | 今泉 今右衛門 | 010201 | 第一システム開発部 | 3 |
10005 | 牧谿 | 010201 | 第一システム開発部 | 3 |
10006 | 関羽 雲長 | 0101 | 人事・総務本部 | 1 |
022. 表領域の使用率(%)
SELECT_CATALOG_ROLE 権限が必要
SELECT T1.tablespace_name , T1.autoextensible , T1.maxbytes / 1024 AS max_kb , T1.tablespace_bytes / 1024 AS tablespace_kb , T2.free_bytes / 1024 AS free_kb , (T1.tablespace_bytes - T2.free_bytes) / 1024 AS used_kb , ROUND((T1.tablespace_bytes - T2.free_bytes) / T1.maxbytes * 100,2) AS "MAX_USED(%)" , ROUND((T1.tablespace_bytes - T2.free_bytes) / T1.tablespace_bytes * 100,2) AS "TABLESPACE_USED(%)" FROM -- Information about database data files ( SELECT tablespace_name , MAX(autoextensible) AS autoextensible , SUM(GREATEST(maxbytes,bytes)) AS maxbytes , SUM(bytes) AS tablespace_bytes FROM sys.dba_data_files GROUP BY tablespace_name ) T1 -- Free extents in all tablespaces LEFT OUTER JOIN ( SELECT tablespace_name, SUM(bytes) AS free_bytes FROM sys.dba_free_space GROUP BY tablespace_name ) T2 ON T1.tablespace_name = T2.tablespace_name ORDER BY T1.tablespace_name |
023. データファイルの使用率(%)
SELECT_CATALOG_ROLE 権限が必要
SELECT T1.file_id , T1.file_name , T1.tablespace_name , T1.autoextensible , GREATEST(T1.maxbytes,T1.bytes) / 1024 AS max_kb , T1.bytes / 1024 AS file_kb , T2.free_bytes / 1024 AS free_kb , (T1.bytes - T2.free_bytes) / 1024 AS used_kb , ROUND((T1.bytes - T2.free_bytes) / GREATEST(T1.maxbytes,T1.bytes) * 100,2) "MAX_USED(%)" , ROUND((T1.bytes - T2.free_bytes) / T1.bytes * 100,2) "FILE_USED(%)" FROM sys.dba_data_files T1 LEFT OUTER JOIN ( SELECT tablespace_name , file_id , SUM(bytes) AS free_bytes FROM sys.dba_free_space GROUP BY tablespace_name , file_id ) T2 ON T1.tablespace_name = T2.tablespace_name AND T1.file_id = T2.file_id ORDER BY T1.file_id |
024. テーブルサイズ(バイト数)
SELECT DISTINCT user AS owner , T1.table_name , T3.comments , T2.status , T1.temporary , T1.num_rows , T1.avg_row_len , T1.avg_row_len * T1.num_rows AS estimated_size , (SELECT COUNT(*) FROM sys.user_tab_columns T WHERE T1.table_name = T.table_name ) AS tab_columns , (SELECT COUNT(*) FROM sys.user_indexes T WHERE T1.table_name = T.table_name ) AS indexeds , (SELECT COUNT(*) FROM sys.user_dependencies T WHERE T1.table_name = T.referenced_name) AS dependencies , (SELECT COUNT(*) FROM sys.user_triggers T WHERE T1.table_name = T.table_name ) AS triggers , T1.tablespace_name , T2.created , MAX(T2.last_ddl_time) OVER(PARTITION BY user, T1.table_name) last_ddl_time -- パーティション用 , T1.last_analyzed FROM sys.user_tables T1 LEFT OUTER JOIN sys.user_objects T2 ON T1.table_name = T2.object_name LEFT OUTER JOIN sys.user_tab_comments T3 ON T1.table_name = T3.table_name WHERE T2.object_name NOT IN (SELECT object_name FROM recyclebin) AND T2.generated = 'N' ORDER BY 1,2 |
025. MONTHS_BETWEEN の罠
MONTHS_BETWEEN は使い方に注意が必要。
WITH v_tab1 AS ( SELECT TO_DATE('2012-01-28') AS start_date, TO_DATE('2012-02-29') AS end_date FROM dual UNION SELECT TO_DATE('2012-01-29') AS start_date, TO_DATE('2012-02-29') AS end_date FROM dual UNION SELECT TO_DATE('2012-01-30') AS start_date, TO_DATE('2012-02-29') AS end_date FROM dual UNION SELECT TO_DATE('2012-01-31') AS start_date, TO_DATE('2012-02-29') AS end_date FROM dual UNION SELECT TO_DATE('2013-01-28') AS start_date, TO_DATE('2013-02-28') AS end_date FROM dual UNION SELECT TO_DATE('2013-01-29') AS start_date, TO_DATE('2013-02-28') AS end_date FROM dual UNION SELECT TO_DATE('2013-01-30') AS start_date, TO_DATE('2013-02-28') AS end_date FROM dual UNION SELECT TO_DATE('2013-01-31') AS start_date, TO_DATE('2013-02-28') AS end_date FROM dual ) SELECT start_date , end_date , TRUNC(MONTHS_BETWEEN(end_date,start_date)) AS v_months_between FROM v_tab1 ORDER BY end_date , start_date |
▽SELECT 結果
START_DATE | END_DATE | V_MONTHS_BETWEEN | |
---|---|---|---|
2012-01-28 | 2012-02-29 | 1 | |
2012-01-29 | 2012-02-29 | 1 | |
2012-01-30 | 2012-02-29 | 0 | ※ |
2012-01-31 | 2012-02-29 | 1 | |
2013-01-28 | 2013-02-28 | 1 | |
2013-01-29 | 2013-02-28 | 0 | ※ |
2013-01-30 | 2013-02-28 | 0 | ※ |
2013-01-31 | 2013-02-28 | 1 |
上記、※印の結果が思った通りの結果であるかどうか判断してから使用すること。
もし意図しない結果であるなら、FUNCTION を作るなど対策を講じる必要がある。
▽CREATE FUNCTION > 『期間の計算』MONTHS_BETWEEN(改)
CREATE OR REPLACE FUNCTION MONTHS_BETWEEN2 ( i_from_date IN DATE , i_to_date IN DATE := SYSDATE , i_parm IN NUMBER := 1 ) -- /******************************************************************************* * Object Name : MONTHS_BETWEEN2 * Description : 『期間の計算』MONTHS_BETWEEN(改) * Arguments : i_from_date IN DATE 開始日 * : i_to_date IN DATE 終了日 * : i_parm IN NUMBER パラメータ * Created : 2012/07/23 * Modified : * : ******************************************************************************/ -- RETURN NUMBER IS v_months_between NUMBER; BEGIN -- 【前提条件】開始日≦終了日であること IF i_from_date > i_to_date THEN RETURN NULL; END IF; /*----------------------------------------------------------------------------*/ /* ▽MONTHS_BETWEEN ストレート /*----------------------------------------------------------------------------*/ IF i_parm = 0 THEN /* ┏━━━━━━━━┳━━━━━━━━━━━━━┓ ┃ ┃開始日 ┃ ┃ ┠─────────────┨ ┃ ┃1月 2月 ┃ ┃ ┠─┬─┬─┬─┬─┬─┬─┨ ┃ ┃27│28│29│30│31│ 1│ 2┃ ┣━━━┯━━┯━╋━┿━┿━┿━┿━┿━┿━┫ ┃終了日│ 2月│26┃ 0│ │ │ │ │ │ ┃ ┃ │ │27┃ 1│ 0│ │ │ 0│ │ ┃ ┃ │ │28┃ │ 1│ 0│ 0│ 1│ 0│ 0┃ ┃ │ 3月│ 1┃ │ │ 1│ 1│ │ 1│ 0┃ ┗━━━┷━━┷━┻━┷━┷━┷━┷━┷━┷━┛ ┏━━━━━━━━┳━━━━━━━━━┓ ┃ ┃開始日 ┃ ┃ ┠─────────┨ ┃ ┃2月 3月 ┃ ┃ ┠─┬─┬─┬─┬─┨ ┃ ┃26│27│28│ 1│ 2┃ ┣━━━┯━━┯━╋━┿━┿━┿━┿━┫ ┃終了日│ 3月│25┃ 0│ │ │ │ ┃ ┃ │ │26┃ 1│ 0│ │ │ ┃ ┃ │ │27┃ │ 1│ 0│ │ ┃ ┃ │ │28┃ │ │ 1│ 0│ ┃ ┃ │ │29┃ │ │ 1│ 0│ ┃ ┃ │ │30┃ │ │ 1│ 0│ ┃ ┃ │ │31┃ │ │ 1│ 0│ ┃ ┃ │ 4月│ 1┃ │ │ │ 1│ 0┃ ┗━━━┷━━┷━┻━┷━┷━┷━┷━┛ */ SELECT + TRUNC(MONTHS_BETWEEN(i_to_date,i_from_date)) INTO v_months_between FROM dual ; END IF; /*----------------------------------------------------------------------------*/ /* ▽翌月同日が1ヶ月となるパターン(同日がなければ前日) /*----------------------------------------------------------------------------*/ IF i_parm = 1 THEN /* ┏━━━━━━━━┳━━━━━━━━━━━━━┓ ┃ ┃開始日 ┃ ┃ ┠─────────────┨ ┃ ┃1月 2月 ┃ ┃ ┠─┬─┬─┬─┬─┬─┬─┨ ┃ ┃27│28│29│30│31│ 1│ 2┃ ┣━━━┯━━┯━╋━┿━┿━┿━┿━┿━┿━┫ ┃終了日│ 2月│26┃ 0│ │ │ │ │ │ ┃ ┃ │ │27┃ 1│ 0│ 0│ 0│ 0│ │ ┃ ┃ │ │28┃ │ 1│ 1│ 1│ 1│ 0│ ┃ ┃ │ 3月│ 1┃ │ │ │ │ │ 1│ 0┃ ┗━━━┷━━┷━┻━┷━┷━┷━┷━┷━┷━┛ ┏━━━━━━━━┳━━━━━━━━━┓ ┃ ┃開始日 ┃ ┃ ┠─────────┨ ┃ ┃2月 3月 ┃ ┃ ┠─┬─┬─┬─┬─┨ ┃ ┃26│27│28│ 1│ 2┃ ┣━━━┯━━┯━╋━┿━┿━┿━┿━┫ ┃終了日│ 3月│25┃ 0│ │ │ │ ┃ ┃ │ │26┃ 1│ 0│ │ │ ┃ ┃ │ │27┃ │ 1│ 0│ │ ┃ ┃ │ │28┃ │ │ 1│ 0│ ┃ ┃ │ │29┃ │ │ 1│ 0│ ┃ ┃ │ │30┃ │ │ 1│ 0│ ┃ ┃ │ │31┃ │ │ 1│ 0│ ┃ ┃ │ 4月│ 1┃ │ │ │ 1│ 0┃ ┗━━━┷━━┷━┻━┷━┷━┷━┷━┛ */ SELECT + MONTHS_BETWEEN(TRUNC(i_to_date,'MM'),TRUNC(i_from_date,'MM')) - CASE WHEN DECODE(i_to_date,LAST_DAY(i_to_date),'31',TO_CHAR(i_to_date,'DD')) < TO_CHAR(i_from_date,'DD') THEN 1 ELSE 0 END INTO v_months_between FROM dual ; END IF; /*----------------------------------------------------------------------------*/ /* ▽翌月同日が1ヶ月となるパターン(同日がなければ翌日) /*----------------------------------------------------------------------------*/ IF i_parm = 2 THEN /* ┏━━━━━━━━┳━━━━━━━━━━━━━┓ ┃ ┃開始日 ┃ ┃ ┠─────────────┨ ┃ ┃1月 2月 ┃ ┃ ┠─┬─┬─┬─┬─┬─┬─┨ ┃ ┃27│28│29│30│31│ 1│ 2┃ ┣━━━┯━━┯━╋━┿━┿━┿━┿━┿━┿━┫ ┃終了日│ 2月│26┃ 0│ │ │ │ │ │ ┃ ┃ │ │27┃ 1│ 0│ │ │ │ │ ┃ ┃ │ │28┃ │ 1│ 0│ 0│ 0│ 0│ ┃ ┃ │ 3月│ 1┃ │ │ 1│ 1│ 1│ 1│ 0┃ ┗━━━┷━━┷━┻━┷━┷━┷━┷━┷━┷━┛ ┏━━━━━━━━┳━━━━━━━━━┓ ┃ ┃開始日 ┃ ┃ ┠─────────┨ ┃ ┃2月 3月 ┃ ┃ ┠─┬─┬─┬─┬─┨ ┃ ┃26│27│28│ 1│ 2┃ ┣━━━┯━━┯━╋━┿━┿━┿━┿━┫ ┃終了日│ 3月│25┃ 0│ │ │ │ ┃ ┃ │ │26┃ 1│ 0│ │ │ ┃ ┃ │ │27┃ │ 1│ 0│ │ ┃ ┃ │ │28┃ │ │ 1│ 0│ ┃ ┃ │ │29┃ │ │ 1│ 0│ ┃ ┃ │ │30┃ │ │ 1│ 0│ ┃ ┃ │ │31┃ │ │ 1│ 0│ ┃ ┃ │ 4月│ 1┃ │ │ │ 1│ 0┃ ┗━━━┷━━┷━┻━┷━┷━┷━┷━┛ */ SELECT + MONTHS_BETWEEN(TRUNC(i_to_date,'MM'),TRUNC(i_from_date,'MM')) - CASE WHEN TO_CHAR(i_from_date,'DD') <= TO_CHAR(i_to_date,'DD') THEN 0 ELSE 1 END INTO v_months_between FROM dual ; END IF; /*----------------------------------------------------------------------------*/ /* ▽翌月前日が1ヶ月となるパターン(定期券方式/民法第143条第2項方式) /*----------------------------------------------------------------------------*/ IF i_parm = 3 THEN /* ┏━━━━━━━━┳━━━━━━━━━━━━━┓ ┃ ┃開始日 ┃ ┃ ┠─────────────┨ ┃ ┃1月 2月 ┃ ┃ ┠─┬─┬─┬─┬─┬─┬─┨ ┃ ┃27│28│29│30│31│ 1│ 2┃ ┣━━━┯━━┯━╋━┿━┿━┿━┿━┿━┿━┫ ┃終了日│ 2月│26┃ 1│ 0│ │ │ │ │ ┃ ┃ │ │27┃ │ 1│ 0│ 0│ 0│ 0│ ┃ ┃ │ │28┃ │ │ 1│ 1│ 1│ 1│ 0┃ ┃ │ 3月│ 1┃ │ │ │ │ │ │ 1┃ ┗━━━┷━━┷━┻━┷━┷━┷━┷━┷━┷━┛ ┏━━━━━━━━┳━━━━━━━━━┓ ┃ ┃開始日 ┃ ┃ ┠─────────┨ ┃ ┃2月 3月 ┃ ┃ ┠─┬─┬─┬─┬─┨ ┃ ┃26│27│28│ 1│ 2┃ ┣━━━┯━━┯━╋━┿━┿━┿━┿━┫ ┃終了日│ 3月│25┃ 1│ 0│ │ │ ┃ ┃ │ │26┃ │ 1│ 0│ │ ┃ ┃ │ │27┃ │ │ 1│ 0│ ┃ ┃ │ │28┃ │ │ 1│ 0│ ┃ ┃ │ │29┃ │ │ 1│ 0│ ┃ ┃ │ │30┃ │ │ 1│ 0│ ┃ ┃ │ │31┃ │ │ │ 1│ 0┃ ┃ │ 4月│ 1┃ │ │ │ │ 1┃ ┗━━━┷━━┷━┻━┷━┷━┷━┷━┛ */ SELECT + MONTHS_BETWEEN(TRUNC(i_to_date,'MM'),TRUNC(i_from_date - 1,'MM')) - CASE WHEN DECODE(i_to_date,LAST_DAY(i_to_date),'31',TO_CHAR(i_to_date,'DD')) < DECODE(i_from_date - 1,LAST_DAY(i_from_date - 1),'31',TO_CHAR(i_from_date - 1,'DD')) THEN 1 ELSE 0 END INTO v_months_between FROM dual ; END IF; /*----------------------------------------------------------------------------*/ /* ▽ロジック確認 /*----------------------------------------------------------------------------*/ /* WITH v_tab1 AS ( SELECT i_from_date, i_to_date FROM ( SELECT TO_DATE('2010-01-27','YYYY-MM-DD') AS i_from_date FROM dual UNION SELECT TO_DATE('2010-01-28','YYYY-MM-DD') AS i_from_date FROM dual UNION SELECT TO_DATE('2010-01-29','YYYY-MM-DD') AS i_from_date FROM dual UNION SELECT TO_DATE('2010-01-30','YYYY-MM-DD') AS i_from_date FROM dual UNION SELECT TO_DATE('2010-01-31','YYYY-MM-DD') AS i_from_date FROM dual UNION SELECT TO_DATE('2010-02-01','YYYY-MM-DD') AS i_from_date FROM dual UNION SELECT TO_DATE('2010-02-02','YYYY-MM-DD') AS i_from_date FROM dual ) T1 , ( SELECT TO_DATE('2010-02-26','YYYY-MM-DD') AS i_to_date FROM dual UNION SELECT TO_DATE('2010-02-27','YYYY-MM-DD') AS i_to_date FROM dual UNION SELECT TO_DATE('2010-02-28','YYYY-MM-DD') AS i_to_date FROM dual UNION SELECT TO_DATE('2010-03-01','YYYY-MM-DD') AS i_to_date FROM dual ) T2 ), v_tab2 AS ( SELECT i_from_date, i_to_date FROM ( SELECT TO_DATE('2010-02-26','YYYY-MM-DD') AS i_from_date FROM dual UNION SELECT TO_DATE('2010-02-27','YYYY-MM-DD') AS i_from_date FROM dual UNION SELECT TO_DATE('2010-02-28','YYYY-MM-DD') AS i_from_date FROM dual UNION SELECT TO_DATE('2010-03-01','YYYY-MM-DD') AS i_from_date FROM dual UNION SELECT TO_DATE('2010-03-02','YYYY-MM-DD') AS i_from_date FROM dual ) T1 , ( SELECT TO_DATE('2010-03-25','YYYY-MM-DD') AS i_to_date FROM dual UNION SELECT TO_DATE('2010-03-26','YYYY-MM-DD') AS i_to_date FROM dual UNION SELECT TO_DATE('2010-03-27','YYYY-MM-DD') AS i_to_date FROM dual UNION SELECT TO_DATE('2010-03-28','YYYY-MM-DD') AS i_to_date FROM dual UNION SELECT TO_DATE('2010-03-29','YYYY-MM-DD') AS i_to_date FROM dual UNION SELECT TO_DATE('2010-03-30','YYYY-MM-DD') AS i_to_date FROM dual UNION SELECT TO_DATE('2010-03-31','YYYY-MM-DD') AS i_to_date FROM dual UNION SELECT TO_DATE('2010-04-01','YYYY-MM-DD') AS i_to_date FROM dual ) T2 ) SELECT i_from_date, i_to_date, months_between2(i_from_date,i_to_date,1) AS v_months_between FROM (SELECT * FROM v_tab1 UNION ALL SELECT * FROM v_tab2) ORDER BY 1,2 */ RETURN v_months_between; EXCEPTION WHEN OTHERS THEN RETURN NULL; END; / SHOW ERRORS |

026. ADD_MONTHS(改)
MONTHS_BETWEEN 同様、ADD_MONTHS も使い方に注意が必要。
CREATE OR REPLACE FUNCTION ADD_MONTHS2 ( i_date IN DATE , i_months IN NUMBER , i_parm IN NUMBER := 1 ) -- /******************************************************************************* * Object Name : ADD_MONTHS2 * Description : 『日付+月数』ADD_MONTHS(改) * Arguments : i_date IN DATE 日付(DD 未満切り捨て) * : i_months IN NUMBER 月数(整数) * : i_parm IN NUMBER パラメータ * Created : 2012/08/08 * Modified : * : ******************************************************************************/ -- RETURN DATE IS v_date DATE; -- 日付 v_add_months DATE; -- 結果 BEGIN v_date := TRUNC(i_date,'DD'); /*----------------------------------------------------------------------------*/ /* ▽ADD_MONTHS ストレート /*----------------------------------------------------------------------------*/ IF i_parm = 0 THEN /* ▽月数 = 12n ▽月数 = 1 ┏━━━━━━━┳━━━━━┓┏━━━━━━━┳━━━┓┏━━━━━━━┳━━━━━┓┏━━━━━━━┳━━━┓ ┃ ┃日付 ┃┃ ┃日付 ┃┃ ┃日付 ┃┃ ┃日付 ┃ ┃ ┠─────┨┃ ┠───┨┃ ┠─────┨┃ ┠───┨ ┃ ┃2月 ┃┃ ┃2月 ┃┃ ┃2月 ┃┃ ┃4月 ┃ ┃ ┠─┬─┬─┨┃ ┠─┬─┨┃ ┠─┬─┬─┨┃ ┠─┬─┨ ┃ ┃27│28│29┃┃ ┃27│28┃┃ ┃27│28│29┃┃ ┃29│30┃ ┣━━┯━━┯━╋━┿━┿━┫┣━━┯━━┯━╋━┿━┫┣━━┯━━┯━╋━┿━┿━┫┣━━┯━━┯━╋━┿━┫ ┃結果│ 2月│27┃○│ │ ┃┃結果│ 2月│27┃○│ ┃┃結果│ 2月│27┃○│ │ ┃┃結果│ 5月│29┃○│ ┃ ┃ │ │28┃ │○│ ┃┃ │ │28┃ │ ┃┃ │ │28┃ │○│○┃┃ │ │30┃ │ ┃ ┃ │ │29┃ │ │○┃┃ │ │29┃ │●┃┃ │ │ ┃ │ │ ┃┃ │ │31┃ │●┃ ┗━━┷━━┷━┻━┷━┷━┛┗━━┷━━┷━┻━┷━┛┗━━┷━━┷━┻━┷━┷━┛┗━━┷━━┷━┻━┷━┛ */ SELECT ADD_MONTHS(v_date,i_months) INTO v_add_months FROM dual ; END IF; /*----------------------------------------------------------------------------*/ /* ▽起算日に応当する日、ないときはその月の末日(民法第143条第2項方式) /*----------------------------------------------------------------------------*/ IF i_parm = 1 THEN /* ▽月数 = 12n ▽月数 = 1 ┏━━━━━━━┳━━━━━┓┏━━━━━━━┳━━━┓┏━━━━━━━┳━━━━━┓┏━━━━━━━┳━━━┓ ┃ ┃日付 ┃┃ ┃日付 ┃┃ ┃日付 ┃┃ ┃日付 ┃ ┃ ┠─────┨┃ ┠───┨┃ ┠─────┨┃ ┠───┨ ┃ ┃2月 ┃┃ ┃2月 ┃┃ ┃2月 ┃┃ ┃4月 ┃ ┃ ┠─┬─┬─┨┃ ┠─┬─┨┃ ┠─┬─┬─┨┃ ┠─┬─┨ ┃ ┃27│28│29┃┃ ┃27│28┃┃ ┃27│28│29┃┃ ┃29│30┃ ┣━━┯━━┯━╋━┿━┿━┫┣━━┯━━┯━╋━┿━┫┣━━┯━━┯━╋━┿━┿━┫┣━━┯━━┯━╋━┿━┫ ┃結果│ 2月│27┃○│ │ ┃┃結果│ 2月│27┃○│ ┃┃結果│ 2月│27┃○│ │ ┃┃結果│ 5月│29┃○│ ┃ ┃ │ │28┃ │○│ ┃┃ │ │28┃ │●┃┃ │ │28┃ │○│○┃┃ │ │30┃ │●┃ ┃ │ │29┃ │ │○┃┃ │ │29┃ │ ┃┃ │ │ ┃ │ │ ┃┃ │ │31┃ │ ┃ ┗━━┷━━┷━┻━┷━┷━┛┗━━┷━━┷━┻━┷━┛┗━━┷━━┷━┻━┷━┷━┛┗━━┷━━┷━┻━┷━┛ */ SELECT CASE WHEN TO_CHAR(v_date,'DD') >= TO_CHAR(ADD_MONTHS(v_date,i_months),'DD') THEN ADD_MONTHS(v_date,i_months) ELSE ADD_MONTHS(TRUNC(v_date,'MM'),i_months) + TO_CHAR(v_date,'DD') - 1 END INTO v_add_months FROM dual ; END IF; /*----------------------------------------------------------------------------*/ /* ▽ロジック確認 /*----------------------------------------------------------------------------*/ /* SELECT i_date, i_months, add_months2(i_date,i_months,1) AS v_add_months FROM ( SELECT TO_DATE('2008-02-27','YYYY-MM-DD') AS i_date, 48 AS i_months FROM dual UNION SELECT TO_DATE('2008-02-28','YYYY-MM-DD') AS i_date, 48 AS i_months FROM dual UNION SELECT TO_DATE('2008-02-29','YYYY-MM-DD') AS i_date, 48 AS i_months FROM dual UNION SELECT TO_DATE('2010-02-27','YYYY-MM-DD') AS i_date, 24 AS i_months FROM dual UNION SELECT TO_DATE('2010-02-28','YYYY-MM-DD') AS i_date, 24 AS i_months FROM dual UNION SELECT TO_DATE('2012-02-27','YYYY-MM-DD') AS i_date, 12 AS i_months FROM dual UNION SELECT TO_DATE('2012-02-28','YYYY-MM-DD') AS i_date, 12 AS i_months FROM dual UNION SELECT TO_DATE('2012-02-29','YYYY-MM-DD') AS i_date, 12 AS i_months FROM dual UNION SELECT TO_DATE('2012-04-29','YYYY-MM-DD') AS i_date, 1 AS i_months FROM dual UNION SELECT TO_DATE('2012-04-30','YYYY-MM-DD') AS i_date, 1 AS i_months FROM dual ) ORDER BY 1 */ RETURN v_add_months; EXCEPTION WHEN OTHERS THEN RETURN NULL; END; / SHOW ERRORS |

027. Oracle SQL Developer で SQL Server に接続する
028. 36進数(BASE36)⇔10進数
『BASE36 → 10進数』変換
CREATE OR REPLACE FUNCTION base36_to_number ( i_base36 VARCHAR2 ) RETURN NUMBER IS v_number NUMBER := 0; v_char CHAR(1); BEGIN -- FOR n IN 1..LENGTHB(i_base36) LOOP v_char := UPPER(SUBSTRB(i_base36,n,1)); IF v_char BETWEEN '0' AND '9' THEN v_number := v_number * 36 + ASCII(v_char) - ASCII('0'); ELSIF v_char BETWEEN 'A' AND 'Z' THEN v_number := v_number * 36 + ASCII(v_char) - ASCII('A') + 10; END IF; END LOOP; -- RETURN v_number; -- EXCEPTION WHEN OTHERS THEN RETURN NULL; END; / SHOW ERRORS |
『10進数 → BASE36』変換
CREATE OR REPLACE FUNCTION number_to_base36 ( i_number NUMBER ) RETURN VARCHAR2 IS v_base36 VARCHAR2(40); v_number NUMBER; v_mod NUMBER(2); BEGIN -- v_number := i_number; -- LOOP v_mod := MOD(v_number,36); IF v_mod < 10 THEN v_base36 := CHR(ASCII('0') + v_mod) || v_base36; ELSE v_base36 := CHR(ASCII('A') + v_mod - 10) || v_base36; END IF; v_number := FLOOR(v_number / 36); EXIT WHEN v_number = 0; END LOOP; -- RETURN v_base36; -- EXCEPTION WHEN OTHERS THEN RETURN NULL; END; / SHOW ERRORS |
029. DDL のログを記録する ⇒ SCHEMA トリガー
▽参考ページ
Oracle Database PL/SQL言語リファレンス 12cリリース1 (12.1) > PL/SQLトリガー
▽DDL を保管するテーブルを作成
CREATE TABLE ddl_log ( ddl_time DATE , ora_sysevent VARCHAR2(20) , ora_dict_obj_owner VARCHAR2(30) , ora_dict_obj_name VARCHAR2(30) , ora_dict_obj_type VARCHAR2(20) , ora_sql_txt CLOB ); |
▽DDL を記録するトリガーを作成
CREATE OR REPLACE TRIGGER insert_ddl_log -- AFTER CREATE OR ALTER OR RENAME OR DROP OR TRUNCATE ON SCHEMA -- ※FLASHBACK 不可 AFTER DDL ON SCHEMA DECLARE sql_text DBMS_STANDARD.ora_name_list_t; n PLS_INTEGER; v_sql_text CLOB; BEGIN n := ora_sql_txt(sql_text); FOR i IN 1..n LOOP v_sql_text := v_sql_text || sql_text(i); END LOOP; INSERT INTO ddl_log SELECT SYSDATE , ora_sysevent , ora_dict_obj_owner , ora_dict_obj_name , ora_dict_obj_type , v_sql_text FROM dual ; END; / SHOW ERRORS |
▽テスト実行
CREATE TABLE zz_test AS SELECT * FROM dual; ALTER TABLE zz_test ADD (dummy2 VARCHAR2(1)); TRUNCATE TABLE zz_test; RENAME zz_test TO zz_test2; DROP TABLE zz_test2 CASCADE CONSTRAINTS; |
おわり
↓NG(GRANT は DDL のため二重起動となり同一トランザクションでは不可)
CREATE OR REPLACE TRIGGER grant_to_user AFTER INSERT ON ddl_log FOR EACH ROW WHEN (NEW.ora_sysevent = 'CREATE' AND NEW.ora_dict_obj_type IN ('TABLE','VIEW')) DECLARE v_sql VARCHAR2(32767); BEGIN v_sql := 'GRANT SELECT ON ' || :NEW.ora_dict_obj_owner || '.' || :NEW.ora_dict_obj_name || ' TO ユーザ'; EXECUTE IMMEDIATE v_sql; END; / SHOW ERRORS |
↓NG(これもダメ)
CREATE OR REPLACE TRIGGER grant_to_user AFTER CREATE ON SCHEMA DECLARE sql_text DBMS_STANDARD.ora_name_list_t; v_sql CLOB; BEGIN IF ora_dict_obj_type IN ('TABLE','VIEW') THEN v_sql := 'GRANT SELECT ON ' || ora_dict_obj_owner || '.' || ora_dict_obj_name || ' TO ユーザ'; EXECUTE IMMEDIATE v_sql; END IF; END; / SHOW ERRORS |
★CREATE されたオブジェクトに対して権限を付与するには、DBMS_SCHEDULER などで定期的に実行させる。
030. データ値をカラム名に使用
▽準備
CREATE TABLE zz_column ( col1 NUMBER , col2 NUMBER , col3 DATE , col4 DATE ); INSERT INTO zz_column VALUES (1000, 2000, '2011-11-01', '2011-12-01'); COMMIT; ┌───┬───┬───┬───┐ カラム │col1 │col2 │col3 │col4 │ ├───┼───┼───┼───┤ データ行│ 1000│ 2000│Nov-11│Dec-11│ └───┴───┴───┴───┘ |
▽ほしい結果
┌───┬───┐ カラム │Nov-11│Dec-11│ ├───┼───┤ データ行│ 1000│ 2000│ └───┴───┘ |
▽SELECT
COLUMN col3 NEW_VALUE v_col3 NOPRINT COLUMN col4 NEW_VALUE v_col4 NOPRINT SELECT TO_CHAR(col3,'Mon-YY','NLS_DATE_LANGUAGE=AMERICAN') AS col3 FROM zz_column; SELECT TO_CHAR(col4,'Mon-YY','NLS_DATE_LANGUAGE=AMERICAN') AS col4 FROM zz_column; SELECT col1 AS "&v_col3" , col2 AS "&v_col4" FROM zz_column ; |
031. アーカイブログのサイズ確認
▽ログモードの確認
SELECT log_mode FROM v$database; |
▽REDO ログ
SELECT * FROM v$log ORDER BY group#; |
▽ARCHIVE ログ
SELECT * FROM v$archived_log WHERE deleted = 'NO' ORDER BY recid; |
▽ARCHIVE ログ/ファイル別
SELECT recid , name , thread# , sequence# , completion_time , TRUNC(blocks * block_size / 1024 / 1024,1) AS "FILE_SIZE(MB)" FROM v$archived_log WHERE deleted = 'NO' ORDER BY 1 ; |
▽ARCHIVE ログ/日別
SELECT SYSDATE , TRUNC(completion_time,'DD') AS arc_date , COUNT(*) AS arc_count , TRUNC(SUM(blocks * block_size / 1024 / 1024),1) AS "FILE_SIZE(MB)" FROM v$archived_log WHERE deleted = 'NO' GROUP BY TRUNC(completion_time,'DD') ORDER BY 2 ; |
▽ディスク使用率/ASM (Automatic Storage Management)
SELECT name, total_mb, free_mb, TRUNC((total_mb - free_mb) / total_mb * 100,1) AS "ディスク使用率" FROM v$asm_diskgroup; |
032. MD5 ファンクション
CREATE OR REPLACE FUNCTION md5 ( i_string IN VARCHAR2 ) RETURN VARCHAR2 IS v_raw VARCHAR2(16); v_md5 VARCHAR2(32); BEGIN v_raw := DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT_STRING => i_string); SELECT RAWTOHEX(v_raw) INTO v_md5 FROM dual; RETURN v_md5; END; / |
▽実行
SELECT md5('テスト') FROM dual; |
▽結果
3F0326F4E56C3F4B54FEEDE9071CAFBF
033. 実行中のプロシージャ名 (OBJECT_NAME) を取得
CREATE OR REPLACE PROCEDURE xx_object_name AUTHID CURRENT_USER IS v_object_name VARCHAR2(30); BEGIN SELECT T2.object_name INTO v_object_name FROM v$session T1 LEFT OUTER JOIN all_objects T2 ON T1.plsql_entry_object_id = T2.object_id WHERE sid = USERENV('SID') ; DBMS_OUTPUT.PUT_LINE('OBJECT_NAME = ' || v_object_name); END; / |
▽実行
SET SERVEROUTPUT ON FORMAT WRAPPED BEGIN xx_object_name; END; / |
※別スキーマから実行してもOK
034. 100レコード作成
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 100; |
035. BLOB インポート/エクスポート (VBScript)

1.テーブル作成
CREATE TABLE blob_test ( file_name VARCHAR2(256) , file_data BLOB ); ALTER TABLE blob_test ADD CONSTRAINT blob_test$pk PRIMARY KEY ( file_name ); |
2.BLOB インポート.vbs
Set fso = CreateObject("Scripting.FileSystemObject") Set objADO = CreateObject("ADODB.Connection") Set objStrm = CreateObject("ADODB.Stream") Set objUtil = CreateObject("CAPICOM.Utilities") '// ADODB.StreamTypeEnum Const adTypeBinary = 1 Const adTypeText = 2 '// Oracle Database 接続 [Microsoft OLE DB Provider for ODBC Drivers] objADO.Open "Provider=MSDASQL; Data Source=********; User ID=********; Password=********;" '// フォルダ内のファイルを取得 For Each objFile In fso.GetFolder(".").Files '// 拡張子が png のファイルのみ処理 If fso.GetExtensionName(objFile) = "png" Then '// ファイル読み込み objStrm.Open objStrm.Type = adTypeBinary objStrm.Position = 0 objStrm.LoadFromFile objFile file_data = objUtil.BinaryToHex(objStrm.Read) objStrm.Close '// INSERT strSQL = "INSERT INTO blob_test VALUES ('" & objFile.Name & "', HEXTORAW('" & file_data & "'))" Set rs = objADO.Execute(strSQL) End If Next objADO.Close MsgBox "おわったよん" WScript.Quit |
3.BLOB エクスポート.vbs
Set objADO = CreateObject("ADODB.Connection") Set objStrm = CreateObject("ADODB.Stream") '// ADODB.StreamTypeEnum Const adTypeBinary = 1 Const adTypeText = 2 '// ADODB.SaveOptionsEnum Const adSaveCreateNotExist = 1 Const adSaveCreateOverWrite = 2 '// Oracle Database 接続 [Microsoft OLE DB Provider for ODBC Drivers] objADO.Open "Provider=MSDASQL; Data Source=********; User ID=********; Password=********;" strSQL = "SELECT * FROM blob_test" Set rs = objADO.Execute(strSQL) Do Until rs.EOF '// Windows のファイル名に適さない文字が入っているときは注意 file_name = rs.Fields("file_name").Value '// ファイル書き出し objStrm.Open objStrm.Type = adTypeBinary objStrm.Position = 0 objStrm.Write rs.Fields("file_data").Value objStrm.SaveToFile file_name, adSaveCreateOverWrite objStrm.Close rs.Movenext Loop objADO.Close MsgBox "おわったんだよん" WScript.Quit |
036. テーブル・ファンクション/開始日と終了日を渡すと、その期間の月数分のレコードを返す
CREATE OR REPLACE PACKAGE table_function AUTHID DEFINER IS -- レコード型 TYPE rec_term IS RECORD ( fy NUMBER -- 年度(4月期首) , half NUMBER -- 半期 , quarter NUMBER -- 四半期 , months NUMBER -- 月数 , ym01 DATE -- 月初日付 ); -- テーブル型 TYPE tbl_term IS TABLE OF rec_term; -- テーブル・ファンクション FUNCTION get_term (i_start_ymd IN DATE, i_end_ymd IN DATE) RETURN tbl_term PIPELINED; END; / CREATE OR REPLACE PACKAGE BODY table_function IS FUNCTION get_term (i_start_ymd IN DATE, i_end_ymd IN DATE) RETURN tbl_term PIPELINED IS pipe_row rec_term; v_start_ymd DATE; v_end_ymd DATE; BEGIN -- v_start_ymd := TRUNC(i_start_ymd,'MM'); v_end_ymd := TRUNC(i_end_ymd ,'MM'); -- IF v_start_ymd <= v_end_ymd THEN FOR rec IN ( SELECT TO_CHAR(ADD_MONTHS(v_start_ymd,LEVEL - 4),'YYYY') * 1 AS fy , TRUNC((TO_CHAR(ADD_MONTHS(v_start_ymd,LEVEL - 4),'MM') + 5) / 6) AS half , TRUNC((TO_CHAR(ADD_MONTHS(v_start_ymd,LEVEL - 4),'MM') + 2) / 3) AS quarter , LEVEL AS months , ADD_MONTHS(v_start_ymd,LEVEL - 1) AS ym01 FROM dual CONNECT BY LEVEL <= MONTHS_BETWEEN(v_end_ymd,v_start_ymd) + 1 ) LOOP pipe_row := rec; PIPE ROW(pipe_row); END LOOP; END IF; RETURN; END; -- END; / |
SELECT * FROM TABLE(table_function.get_term('2000-01-31','2016-08-31')); |
▽SELECT 結果
FY | HALF | QUARTER | MONTHS | YM01 |
---|---|---|---|---|
1999 | 2 | 4 | 1 | 2000-01-01 |
1999 | 2 | 4 | 2 | 2000-02-01 |
1999 | 2 | 4 | 3 | 2000-03-01 |
2000 | 1 | 1 | 4 | 2000-04-01 |
2000 | 1 | 1 | 5 | 2000-05-01 |
2000 | 1 | 1 | 6 | 2000-06-01 |
2000 | 1 | 2 | 7 | 2000-07-01 |
2000 | 1 | 2 | 8 | 2000-08-01 |
2000 | 1 | 2 | 9 | 2000-09-01 |
2000 | 2 | 3 | 10 | 2000-10-01 |
2000 | 2 | 3 | 11 | 2000-11-01 |
2000 | 2 | 3 | 12 | 2000-12-01 |
2000 | 2 | 4 | 13 | 2001-01-01 |
2000 | 2 | 4 | 14 | 2001-02-01 |
2000 | 2 | 4 | 15 | 2001-03-01 |
・・・ |   |   |   |   |
2016 | 1 | 1 | 196 | 2016-04-01 |
2016 | 1 | 1 | 197 | 2016-05-01 |
2016 | 1 | 1 | 198 | 2016-06-01 |
2016 | 1 | 2 | 199 | 2016-07-01 |
2016 | 1 | 2 | 200 | 2016-08-01 |
037. 「EXP-00003: セグメントに対する記憶域定義がありません」対処法
「EXP-00003: セグメントに対する記憶域定義がありません」
下記の条件1~4を全て満たすとき、このメッセージが出る。
【条件1】 Oracle Database のバージョンが 11.2 以降
-- Oracle Database のバージョン SELECT * FROM v$version; |
【条件2】 パラメータの設定/DEFERRED_SEGMENT_CREATION = TRUE(デフォルト)
-- システムパラメータ SELECT * FROM v$parameter WHERE name = 'deferred_segment_creation'; -- システムパラメータ変更(ALTER SESSION 可) ALTER SYSTEM SET DEFERRED_SEGMENT_CREATION = FALSE; |

(抜粋)
DEFERRED_SEGMENT_CREATIONは、遅延セグメント作成のセマンティクスを指定します。 trueを設定すると、表のセグメントと依存オブジェクト(LOB、索引)は、表に最初の行が挿入されるまで作成されません。 一連の表を作成する場合で、多数の表に移入が行われないことが明らかなときは、 その前にこのパラメータにtrueを設定することを検討してください。 この設定によって、ディスク領域が節約され、インストール時間も短縮されます。 |
【条件3】 セグメントが存在しないテーブル ≒ データが INSERT されたことのないテーブル
-- セグメントが存在しないテーブル SELECT * FROM user_tables WHERE segment_created = 'NO'; |
【条件4】 EXP コマンドでエクスポート
【対処法1】 DBMS_SPACE_ADMIN が使える場合
-- 遅延セグメント作成が設定された表(および表の一部)とその依存オブジェクトに対してセグメントを実体化します。 EXECUTE DBMS_SPACE_ADMIN.MATERIALIZE_DEFERRED_SEGMENTS (schema_name => 'スキーマ'); |
-- 空の表(または表の一部)とその依存オブジェクトからセグメントを削除します。 EXECUTE DBMS_SPACE_ADMIN.DROP_EMPTY_SEGMENTS (schema_name => 'スキーマ'); |
-- SYS で実行/もしくは GRANT EXECUTE で権限付与 ⇒ 他のスキーマも更新できるため NG ⇒ SYS で実行すべし /* GRANT EXECUTE ON DBMS_SPACE_ADMIN TO スキーマ; REVOKE EXECUTE ON DBMS_SPACE_ADMIN FROM スキーマ; */ |

【対処法2】 DBMS_SPACE_ADMIN が使えない場合
-- 統計情報の収集 EXECUTE DBMS_STATS.GATHER_SCHEMA_STATS (USER); -- セグメントが空のテーブルにデータを INSERT & DELETE SET SERVEROUTPUT ON FORMAT WRAPPED DECLARE v_sql VARCHAR2(32767); -- SQL BEGIN FOR tab IN (SELECT table_name FROM user_tables WHERE segment_created = 'NO') LOOP -- v_sql := 'INSERT INTO ' || tab.table_name || ' VALUES ('; FOR col IN (SELECT * FROM user_tab_columns WHERE table_name = tab.table_name ORDER BY column_id) LOOP IF col.column_id > 1 THEN v_sql := v_sql || ','; END IF; IF col.data_type LIKE '%CHAR%' THEN v_sql := v_sql || '''1'''; ELSIF col.data_type LIKE '%LOB%' THEN v_sql := v_sql || '''1'''; ELSIF col.data_type LIKE 'TIME%' THEN v_sql := v_sql || 'SYSDATE'; ELSIF col.data_type = 'DATE' THEN v_sql := v_sql || 'SYSDATE'; ELSE v_sql := v_sql || '1'; END IF; END LOOP; v_sql := v_sql || ')'; -- BEGIN EXECUTE IMMEDIATE v_sql; DBMS_OUTPUT.PUT_LINE(tab.table_name); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('>> FAILURE : ' || tab.table_name); DBMS_OUTPUT.PUT_LINE(v_sql); END; -- EXECUTE IMMEDIATE 'DELETE FROM ' || tab.table_name; -- COMMIT; -- END LOOP; END; / |
038. オプティマイザのバージョン 11.2.0.3 以降で挙動不審になる
オプティマイザのバージョン 11.2.0.3 以降、パフォーマンスが大きく劣化することがある。
▼パフォーマンス問題が起きるケース
-- SELECT句で副問い合わせをしたとき SELECT 列名1, (SELECT 列名2 FROM ... ) FROM ... ; SELECT 列名1, ファンクション名 (列名1) FROM ... ; |
実行計画や AUTOTRACE が 11.2.0.2 以前と大きく変わり、recursive calls(再帰コール数)が増大することがある。
▽解決策1
-- SQLレベル SELECT /*+ OPT_PARAM('_complex_view_merging' 'FALSE') */ * FROM ... ; -- セッションレベル ALTER SESSION SET "_complex_view_merging" = FALSE; -- システムレベル ALTER SYSTEM SET "_complex_view_merging" = FALSE; |
▽解決策2
-- SQLレベル SELECT /*+ OPT_PARAM('optimizer_features_enable' '11.2.0.2') */ * FROM ... ; SELECT /*+ optimizer_features_enable('11.2.0.2') */ * FROM ... ; -- セッションレベル ALTER SESSION SET "optimizer_features_enable" = '11.2.0.2'; -- システムレベル ALTER SYSTEM SET "optimizer_features_enable" = '11.2.0.2'; |
▽解決策3
-- ORDER BY を入れると遅くなるもの SELECT /*+ FIRST_ROWS(1) */ * FROM ... ; |
▽解決策4
-- 統計情報が取れないものを結合すると遅くなるもの(TABLE FUNCTION など CARDINALITY が正しく取れない場合) SELECT /*+ DYNAMIC_SAMPLING(テーブル名 2) */ * FROM ... ; |
▼パラメータの確認
-- オプティマイザ SELECT * FROM v$ses_optimizer_env WHERE sid = USERENV('SID'); SELECT * FROM v$sql_optimizer_env; SELECT * FROM v$sys_optimizer_env; SELECT * FROM v$parameter WHERE name LIKE '%optim%'; -- 隠しパラメータ SELECT * FROM v$parameter WHERE name LIKE '\_%' ESCAPE '\'; _complex_view_merging DEFAULT TRUE _optim_peek_user_binds DEFAULT TRUE _optimizer_use_feedback DEFAULT TRUE |