STS(SQL Tuning Set) で SELECT文のみをキャプチャしてみる(Oracle Database)

STS(SQL Tuning Set) は Oracle Database の SQL情報(SQLテキスト、実行計画、性能統計、バインド変数など)を保持するオブジェクトです。

> SQLチューニング・ガイド 23ai
> 24 SQLチューニング・セットでのワークロードの取得
> https://docs.oracle.com/cd/G11854_01/tgsql/managing-sql-tuning-sets.html

今回は STS にキャプチャされる SQL を SELECT文のみにしてみます彡(^)(^)

# 1. データの準備

下記の SQL でデータを準備します。

```sql
-- Create Table
DROP TABLE TEST_TABLE;
CREATE TABLE TEST_TABLE (
   C1 NUMBER
  ,C2 VARCHAR2(100)
  ,C3 DATE
);

-- Dummy Data
INSERT INTO TEST_TABLE
SELECT LEVEL
     , 'TEST' || TO_CHAR(LEVEL)
     , TRUNC(SYSDATE, 'DD') + (LEVEL/24/60/60)
  FROM DUAL
CONNECT BY LEVEL <= 10000;

-- Commit
COMMIT;

-- Primary Key
ALTER TABLE TEST_TABLE ADD CONSTRAINT TEST_TABLE_PK PRIMARY KEY(C1) USING INDEX;
```

結果は以下のとおりです。

```sql
Table dropped.

Table created.

10000 rows created.

Commit complete.

Table altered.
```

# 2. SQL の実行

SQL を実行してみます。試しにデータをカウントしてみます。

```sql
SELECT COUNT(*) FROM TEST_TABLE;
SELECT COUNT(*) FROM TEST_TABLE WHERE C3 >= TRUNC(SYSDATE, 'DD') + 2/24;
```

結果は以下の通りです。

```sql
SQL> SELECT COUNT(*) FROM TEST_TABLE;

  COUNT(*)
----------
     10000

SQL> SELECT COUNT(*) FROM TEST_TABLE WHERE C3 >= TRUNC(SYSDATE, 'DD') + 2/24;

  COUNT(*)
----------
      2801

SQL>
```

# 3. STS のキャプチャと確認(command_type属性でフィルタしないバージョン)

まず command_type属性でフィルタしないバージョンで STS をキャプチャしてみます。
スキーマ(parsing_schema_name)はフィルタします。以下のコマンドを実行します。

```sql
-- Drop and create the tuning set
EXEC DBMS_SQLSET.DROP_SQLSET('ayu_sts');
EXEC DBMS_SQLSET.CREATE_SQLSET('ayu_sts');

-- Load the sql tuning set from the shared SQL area
DECLARE
  cur DBMS_SQLSET.SQLSET_CURSOR;
BEGIN
  OPEN cur FOR
    SELECT VALUE(P)
      FROM table(
        DBMS_SQLSET.SELECT_CURSOR_CACHE(
           'parsing_schema_name = ''AYSHIBAT2''', -- basic_filter
           NULL,                                  -- object_filter
           NULL,                                  -- ranking_mesure1
           NULL,                                  -- ranking_mesure2
           NULL,                                  -- ranking_mesure3
           1,                                     -- result_percentage
           NULL,                                  -- result_limit
           NULL,                                  -- attribute_list
           DBMS_SQLSET.NO_RECURSIVE_SQL           -- recursive_sql
        )
     ) P;
  DBMS_SQLSET.LOAD_SQLSET(
    sqlset_name => 'ayu_sts',
    populate_cursor => cur
  );
END;
/ 
```

結果は以下の通りです。

```sql
PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.
```

DBMS_SQLSET.SELECT_SQLSETファンクションで STS の中身を確認してみます。

```sql
SET LINESIZE 300;
SET PAGESIZE 100;
COL SQL_ID FOR A15
COL SQL_TEXT FOR A50 TRUNC
COL PARSING_SCHEMA_NAME FOR A15
COL MODULE FOR A10 TRUNC
SELECT PARSING_SCHEMA_NAME
     , MODULE
     , SQL_ID
     , PLAN_HASH_VALUE
     , SQL_TEXT
  FROM TABLE(DBMS_SQLSET.SELECT_SQLSET('ayu_sts'))
 ORDER BY 1, 2, 3, 4;
```

結果は以下の通りです。

```sql
PARSING_SCHEMA_ MODULE     SQL_ID          PLAN_HASH_VALUE SQL_TEXT
--------------- ---------- --------------- --------------- --------------------------------------------------
AYSHIBAT2       SQL*Plus   00qnpnwg1zkv0                 0 DECLARE
AYSHIBAT2       SQL*Plus   0p9169r2h1vy4                 0 DECLARE
AYSHIBAT2       SQL*Plus   1q5txdwnv3adr         711311523 SELECT COUNT(*) FROM TEST_TABLE WHERE C3 <= SYSDAT
AYSHIBAT2       SQL*Plus   1rzb7pxq1ysg5        2558420059 SELECT command_type, command_name
AYSHIBAT2       SQL*Plus   2mryuw87nvxxf        3967443709 SELECT PARSING_SCHEMA_NAME
AYSHIBAT2       SQL*Plus   2q983ru5vx8vn        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLSET.SELECT_CUR
AYSHIBAT2       SQL*Plus   2yygdv3dqjdc8        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLSET.SELECT_CUR
AYSHIBAT2       SQL*Plus   330ruq40ntq81                 0 DECLARE
AYSHIBAT2       SQL*Plus   3dqh192z0yagk                 0 DECLARE
AYSHIBAT2       SQL*Plus   3u5cbhpg17tud                 0 DECLARE
AYSHIBAT2       SQL*Plus   43tdm52www26p                 0 BEGIN DBMS_SQLSET.DROP_SQLSET('ayu_sts'); END;
AYSHIBAT2       SQL*Plus   4hcb48104uchb                 0 DECLARE
AYSHIBAT2       SQL*Plus   4mt4k256t4ufh        1236776825 INSERT INTO TEST_TABLE
：
AYSHIBAT2                  gn6mqxp5gdnct         711311523 SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHER

49 rows selected.
```

DECLARE文やINSERT文などいろいろ取れています。

# 4. STS のキャプチャと確認(command_type属性でSELECTのみキャプチャするバージョン)

次は command_type属性で SELECT文のみをフィルタして STS をキャプチャします。
後述しますが SELECT文 の command_type属性 は 3(※) となります。
※バージョンやプラットフォーム(CPU, OS)によって異なる可能性あり
スキーマ(parsing_schema_name)もフィルタします。以下のコマンドを実行します。

```sql
-- Drop and create the tuning set
EXEC DBMS_SQLSET.DROP_SQLSET('ayu_sts');
EXEC DBMS_SQLSET.CREATE_SQLSET('ayu_sts');

-- Load the sql tuning set from the shared SQL area
DECLARE
  cur DBMS_SQLSET.SQLSET_CURSOR;
BEGIN
  OPEN cur FOR
    SELECT VALUE(P)
      FROM table(
        DBMS_SQLSET.SELECT_CURSOR_CACHE(
           'parsing_schema_name = ''AYSHIBAT2'' and command_type = 3', -- basic_filter
           NULL,                                  -- object_filter
           NULL,                                  -- ranking_mesure1
           NULL,                                  -- ranking_mesure2
           NULL,                                  -- ranking_mesure3
           1,                                     -- result_percentage
           NULL,                                  -- result_limit
           NULL,                                  -- attribute_list
           DBMS_SQLSET.NO_RECURSIVE_SQL           -- recursive_sql
        )
     ) P;
  DBMS_SQLSET.LOAD_SQLSET(
    sqlset_name => 'ayu_sts',
    populate_cursor => cur
  );
END;
/ 
```

結果は以下の通りです。

```sql
PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.
```

DBMS_SQLSET.SELECT_SQLSETファンクションで STS の中身を確認してみます。

```sql
SET LINESIZE 300;
SET PAGESIZE 100;
COL SQL_ID FOR A15
COL SQL_TEXT FOR A50 TRUNC
COL PARSING_SCHEMA_NAME FOR A15
COL MODULE FOR A10 TRUNC
SELECT PARSING_SCHEMA_NAME
     , MODULE
     , SQL_ID
     , PLAN_HASH_VALUE
     , SQL_TEXT
  FROM TABLE(DBMS_SQLSET.SELECT_SQLSET('ayu_sts'))
 ORDER BY 1, 2, 3, 4;
```

結果は以下の通りです。

```sql
PARSING_SCHEMA_ MODULE     SQL_ID          PLAN_HASH_VALUE SQL_TEXT
--------------- ---------- --------------- --------------- --------------------------------------------------
AYSHIBAT2       SQL*Plus   1q5txdwnv3adr         711311523 SELECT COUNT(*) FROM TEST_TABLE WHERE C3 <= SYSDAT
AYSHIBAT2       SQL*Plus   1rzb7pxq1ysg5        2558420059 SELECT command_type, command_name
AYSHIBAT2       SQL*Plus   2mryuw87nvxxf        3967443709 SELECT PARSING_SCHEMA_NAME
AYSHIBAT2       SQL*Plus   2q983ru5vx8vn        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLSET.SELECT_CUR
AYSHIBAT2       SQL*Plus   2yygdv3dqjdc8        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLSET.SELECT_CUR
AYSHIBAT2       SQL*Plus   4qt314uwkk1f4         711311523 SELECT COUNT(*) FROM TEST_TABLE WHERE C3 >= TRUNC(
AYSHIBAT2       SQL*Plus   5d1djbtbdwhpa        3967443709 SELECT PARSING_SCHEMA_NAME
AYSHIBAT2       SQL*Plus   5na8zpmv29pdj         711311523 SELECT COUNT(*) FROM TEST_TABLE WHERE C3 >= TRUNC(
AYSHIBAT2       SQL*Plus   5ut0916yg0dcd         711311523 SELECT COUNT(*) FROM TEST_TABLE WHERE C3 >= SYSDAT
AYSHIBAT2       SQL*Plus   6183guz8tqpyn        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLSET.SELECT_CUR
AYSHIBAT2       SQL*Plus   65j92m5kzbtvc        1200554957 SELECT COUNT(*) FROM TEST_TABLE
AYSHIBAT2       SQL*Plus   66kvgus6875ya        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLTUNE.SELECT_CU
AYSHIBAT2       SQL*Plus   6c9p04fh9qd79        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLSET.SELECT_CUR
AYSHIBAT2       SQL*Plus   6kpkfs4w3a834        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLSET.SELECT_CUR
AYSHIBAT2       SQL*Plus   7wy3kr47a118w        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLSET.SELECT_CUR
AYSHIBAT2       SQL*Plus   8g82q15uxwwwu        1388734953 SELECT DBMS_STATS.REPORT_COL_USAGE('AYSHIBAT2', NU
AYSHIBAT2       SQL*Plus   8r18cn95tj5nn        3967443709 SELECT PARSING_SCHEMA_NAME
AYSHIBAT2       SQL*Plus   8ttyrqwgmygb7         711311523 SELECT COUNT(*) FROM TEST_TABLE WHERE C3 >= TRUNC(
AYSHIBAT2       SQL*Plus   99hvq2ys1gnbz        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLSET.SELECT_CUR
AYSHIBAT2       SQL*Plus   bcapxw0b5k2mp        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLTUNE.SELECT_CU
AYSHIBAT2       SQL*Plus   bch0bp2z3kqmu         711311523 SELECT COUNT(*) FROM TEST_TABLE WHERE C3 >= TRUNC(
AYSHIBAT2       SQL*Plus   cbnbjy0mjs2fc         711311523 SELECT COUNT(*) FROM TEST_TABLE WHERE C3 >= SYSDAT
AYSHIBAT2       SQL*Plus   ddjzaqw7u2pcf         711311523 SELECT COUNT(*) FROM TEST_TABLE WHERE C3 >= TRUNC(
AYSHIBAT2       SQL*Plus   gnq1uf4um6wcv         711311523 SELECT COUNT(*) FROM TEST_TABLE WHERE C3 >= SYSDAT
AYSHIBAT2       SQL*Plus   gq8tr437y1py6        1869011320 SELECT VALUE(P) FROM TABLE( DBMS_SQLTUNE.SELECT_CU
AYSHIBAT2       sqlplus@lo 6u5zqzz2nm55c        1388734953 SELECT DECODE(USER, 'XS$NULL',  XS_SYS_CONTEXT('XS
AYSHIBAT2                  2g0n96st49uu8         711311523 SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHER
AYSHIBAT2                  67h66vdzzqkjc         711311523 SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHER
AYSHIBAT2                  af2pwyhxwr6sx         711311523 SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHER
AYSHIBAT2                  cffz93mg1zxq7         711311523 SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHER
AYSHIBAT2                  d4tpdhq8vb3f5         711311523 SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHER
AYSHIBAT2                  gn6mqxp5gdnct         711311523 SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHER

32 rows selected.
```

SELECT文のみをフィルタしてキャプチャできました。

# 5. SQLSET_ROWオブジェクト と COMMAND_TYPE属性

SQLSET_ROWオブジェクトの定義は下記マニュアルに記載されています。

> PL/SQLパッケージおよびタイプ・リファレンス 23ai
> 171.3.1 SQLSET_ROWオブジェクト・タイプ
> https://docs.oracle.com/cd/G11854_01/arpls/DBMS_SQLTUNE.html#GUID-1F4AFB03-7B29-46FC-B3F2-CB01EC36326C

parsing_schema_name, sql_id, command_type など様々な属性が定義されています。
SQLSET_ROWオブジェクトに定義された属性は STS のフィルタ条件などで活用可能です。

command_type属性に指定する数値は V$SQLCOMMAND動的ビューを参照します。

> データベース・リファレンス 23ai
> 10.91 V$SQLCOMMAND
> https://docs.oracle.com/cd/G11854_01/refrn/V-SQLCOMMAND.html

V$SQLCOMMANDビューを確認してみます。

```sql
SQL> SELECT * FROM V$SQLCOMMAND;

COMMAND_TYPE COMMAND_NAME         CON_ID
------------ ---------------- ----------
           0                           0
           1 CREATE TABLE              0
           2 INSERT                    0
           3 SELECT                    0
           4 CREATE CLUSTER            0
           5 ALTER CLUSTER             0
           6 UPDATE                    0
           7 DELETE                    0
：
```

SELECT の COMMAND_TYPE が 3 なのが確認できました。

# 6. まとめ

SQLSET_ROWオブジェクトのcommand_type属性でSELECT文のみをキャプチャできました。
それ以外の組み合わせもフィルタしてキャプチャできるので、活用して下さいね彡(^)(^)