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
|
