oracle dbは自身に利用実績が少ない為、メモ。
目次
事前準備
テスト用 table 作成
create table TEST_TBL_1 ( num_1 NUMBER(10), num_2 NUMBER(10), str_1 VARCHAR2(10), str_2 VARCHAR2(10), PRIMARY KEY (num_1) ); CREATE INDEX TEST_TBL_1_STR_1 ON TEST_TBL_1(str_1); create table TEST_TBL_2 ( num_1 NUMBER(10), num_2 NUMBER(10), str_1 VARCHAR2(10), str_2 VARCHAR2(10), PRIMARY KEY (num_1) ); CREATE INDEX TEST_TBL_2_STR_1 ON TEST_TBL_2(str_1);
作成したtabl定義や、pkey、index確認。
まず、カラム定義
SQL> DESCRIBE TEST_TBL_1; Name Null? Type -------------------------- -------- ------------- NUM_1 NOT NULL NUMBER(10) NUM_2 NUMBER(10) STR_1 VARCHAR2(10) STR_2 VARCHAR2(10) SQL> DESCRIBE TEST_TBL_2; Name Null? Type -------------------------- -------- ------------ NUM_1 NOT NULL NUMBER(10) NUM_2 NUMBER(10) STR_1 VARCHAR2(10) STR_2 VARCHAR2(10)
次に、primary key
SQL> column constraint_name format a20 column table_name format a20 column column_name format a20 SELECT c.table_name, cc.column_name,constraint_type FROM user_constraints c JOIN user_cons_columns cc ON (c.table_name =cc.table_name AND c.constraint_name=cc.constraint_name) WHERE c.table_name in ('TEST_TBL_1','TEST_TBL_2') ORDER BY cc.table_name, cc.column_name; TABLE_NAME COLUMN_NAME C -------------------- -------------------- - TEST_TBL_1 NUM_1 P TEST_TBL_2 NUM_1 P
最後に、index
SQL> column INDEX_NAME format a16 column INDEX_TYPE format a10 column TABLE_NAME format a10 SELECT INDEX_NAME, INDEX_TYPE, TABLE_NAME, TABLE_TYPE, UNIQUENESS, LAST_ANALYZED FROM DBA_INDEXES WHERE table_name in ('TEST_TBL_1','TEST_TBL_2'); INDEX_NAME INDEX_TYPE TABLE_NAME TABLE_TYPE UNIQUENES LAST_ANAL ---------------- ---------- ---------- ----------- --------- --------- SYS_C007451 NORMAL TEST_TBL_1 TABLE UNIQUE 26-DEC-20 TEST_TBL_1_STR_1 NORMAL TEST_TBL_1 TABLE NONUNIQUE 26-DEC-20 SYS_C007452 NORMAL TEST_TBL_2 TABLE UNIQUE 26-DEC-20 TEST_TBL_2_STR_1 NORMAL TEST_TBL_2 TABLE NONUNIQUE 26-DEC-20
テストテーブルへのデータinsert
先程のエントリにある python script の通りです。
念の為?、indexを再構築
ALTER INDEX TEST_TBL_1_STR_1 REBUILD; ALTER INDEX TEST_TBL_2_STR_1 REBUILD;
チューニングの為のTIPS (調査・分析)
V$SQLSTATS による統計情報
日々、実行されるSQLのうち遅いSQLを抽出するには、 以下のように V$SQLSTATS を参照すると良いかと思います。
以下では、一回あたりの実行時間(マイクロ秒)が長いSQL 上位5種を出力しています。
EXECUTE DBMS_STATS.GATHER_DATABASE_STATS; SELECT * FROM( SELECT SQL_TEXT, ELAPSED_TIME/EXECUTIONS, LAST_ACTIVE_TIME FROM V$SQLSTATS WHERE EXECUTIONS > 0 ORDER BY ELAPSED_TIME/EXECUTIONS desc) WHERE ROWNUM <= 5; SQL_TEXT -------------------------------------------------------------------------------- ELAPSED_TIME/EXECUTIONS LAST_ACTI ----------------------- --------- call dbms_stats.gather_database_stats_job_proc ( ) 4154223.47 26-DEC-20 select count(*) from wri$_optstat_opr o, wri$_optstat_opr_tasks t where o.id = t .op_id(+) and o.operation = 'gather_database_stats (auto)' and (not regexp_like( extract(xmltype('<notes>' || o.notes || '</notes>'), '//error'), '^<error>O RA-200[0-9][0-9]') or not regexp_like( extract(xmltype('<notes>' || t.notes | | '</notes>'), '//error'), '^<error>ORA-200[0-9][0-9]')) 3094077 26-DEC-20 SQL_TEXT -------------------------------------------------------------------------------- ELAPSED_TIME/EXECUTIONS LAST_ACTI ----------------------- --------- begin prvt_hdm.auto_execute( :dbid, :inst_num , :end_snap_id ); end; 1023710 26-DEC-20 select /*+ no_parallel_index(t, "I_WRI$_OPTSTAT_H_OBJ#_ICOL#_ST") dbms_stats c ursor_sharing_exact use_weak_name_resl dynamic_sampling(0) no_monitoring xmlinde x_sel_idx_tbl opt_param('optimizer_inmemory_aware' 'false') no_substrb_pad no_e xpand index(t,"I_WRI$_OPTSTAT_H_OBJ#_ICOL#_ST") */ count(*) as nrw,count(distinc t sys_op_lbid(14364,'L',t.rowid)) as nlb,count(distinct hextoraw(sys_op_descend( SQL_TEXT -------------------------------------------------------------------------------- ELAPSED_TIME/EXECUTIONS LAST_ACTI ----------------------- --------- "OBJ#")||sys_op_descend("INTCOL#")||sys_op_descend("SYS_NC00018$")||sys_op_desce nd("COLNAME"))) as ndk,sys_op_countchg(substrb(t.rowid,1,15),1) as clf from "SYS "."WRI$_OPTSTAT_HISTGRM_HISTORY" t where "OBJ#" is not null or "INTCOL#" is not null or "SYS_NC00018$" is not null or "COLNAME" is not null 880529 26-DEC-20 MERGE /*+ dynamic_sampling(ST 4) dynamic_sampling_est_cdn(ST) OPT _PARAM('_parallel_syspls_obey_force' 'false') */ INTO STATS_TARGET$ ST USING (SELECT CASE WHEN DBMS_STATS_INTERNAL.BITCLR(AFLAGS, :B20 + :B23 ) = :B : 5 rows selected.
「EXECUTE DBMS_STATS.GATHER_DATABASE_STATS;」は不要かも
実行計画確認 - 「explain plan for」「autotrace」
mysql の explain analyze と異なり、 まず「explain plan for ~」で実行計画を登録し、 その後、「SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());」で参照します。
参考として、出力結果は以下に記載していますが、 実際に動かしながら、確認する方が理解しやすいと思います。
SQL> explain plan for select count(*) from test_tbl_1 t_1 join test_tbl_2 t_2 on (t_1.num_1=t_2.num_1) where t_1.str_1 like 'A%'; 2 3 4 5 Explained. SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY()) ; PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 3343162413 ------------------------------------------------------------------------------ | Id |Operation |Name |Rows |Bytes| Cost (%CPU)| Time | ------------------------------------------------------------------------------ PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------ | 0 |SELECT STATEMENT | | 1| 14| 528 (2)| 00:00:01 | | 1 | SORT AGGREGATE | | 1| 14| | | |* 2 | HASH JOIN | | 2559|35826| 528 (2)| 00:00:01 | |* 3 | TABLE ACCESS FULL |TEST_TBL_1 | 2559|23031| 12 (0)| 00:00:01 | | 4 | INDEX FAST FULL SCAN|SYS_C007452|1000K|4882K| 514 (1)| 00:00:01 | ------------------------------------------------------------------------------ PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------ 2 - access("T_1"."NUM_1"="T_2"."NUM_1") 3 - filter("T_1"."STR_1" LIKE 'A%')
「explain plan for ~」の他、実際にSQLを実行した結果から表示する 「set autotrace on;」もあります。
set autotrace on; select * from test_tbl_2 where str_1 like 'A%'; set autotrace off;
チューニングの為のTIPS (対策実施)
ヒント句による index 指定
mysqlでは「using index~」のように記載していましたが、 oracleではヒント句において「/+ INDEX(~ ~) /」のように指定します。
以下の例では、先程のSELECT文では「TABLE ACCESS FULL」がありましたが 「INDEX RANGE SCAN」で効率化されていることが分かります。
SQL> explain plan for SELECT /*+ INDEX(tbl TEST_TBL_2_STR_1) */ * from TEST_TBL_2 tbl where str_1 = 'ABCD'; Explained. SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY()) ; PLAN_TABLE_OUTPUT --------------------------- Plan hash value: 3456234334 ------------------------------------------------------------------------------------------ |Id |Operation |Name |Rows|Bytes|Cost(%CPU)|Time | ------------------------------------------------------------------------------------------ PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------ | 0|SELECT STATEMENT | |3959|75221|2330 (0)|00:00:01| | 1| TABLE ACCESS BY INDEX ROWID BATCHED|TEST_TBL_2 |3959|75221|2330 (0)|00:00:01| |* 2| INDEX RANGE SCAN |TEST_TBL_2_STR_1|3959| | 11 (0)|00:00:01| ------------------------------------------------------------------------------------------ PLAN_TABLE_OUTPUT --------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("STR_1"='ABCD')
オプティマイザ統計情報の移行
RDBMSは統計情報を参照し、実行計画を作成しますが、 開発環境<->本番環境等、オプティマイザ統計情報の移行を 行った方が良いケースもあります。
試していませんが、以下のurlが分かりやすいです。
パーティショニング
別のエントリに記載しました
oracle database 12c の range パーティショニングを試す - end0tknr's kipple - web写経開発