« Lionがでたとおもったら、VirtualBox 4.1も出てたのね。 | トップページ | いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング7 おまけ »

2011年7月24日 (日)

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #6

2011/7/28追記
GROUPING SETSで異常に遅かったのは使い方わるいなーと m(_ _)m WITH句で一時表1つ使うのと同じぐらいにはなるかもね。と(次回書きます)

前回のつづきでーす。

前回のは、元々がイケテない、かつ、大人の事情でパーティションタイプ変更とか、マテリアライズドビューとか、パラレルクエリーもだーめー。と言いつつ、なんとか、できる限り早くしてくれーという、匂いの元を絶たないで別の匂いでごまかしてくれー的なことだったのですが、いろいろと考えた末、WITH句を使えばなんとかいい感じになりました。でも、元が元だけにほぼ限界ですね。

大人の事情で男らしくずばーっとパーティションタイプ変更することができないとか言ってた方々も、この結果をもって、交渉のテーブルにつけるんじゃないかな。検討祈る :)
この結果で納得してもらえるかは感知しませんからねw 最後の手としてパーティションタイプ変更を残しておいてもいいいと思いますし。;)

ということで、今回は、他の方法との比較編。

前回、grouping setsでもいけそーな感じがすると書いたのですが、grouping setsの場合裏の動きはOracleにお任せなので…

まあ、見てみましょう。


最初は、シンプルにgrouping setsに置き換えて見ました。(深く考えてないので余計な部分はまだまだありそうですがw )

実行計画と処理時間、アクセスブロック数みて少々意外な結果に驚いたんですよ。 

ダメダメだった元のクエリより処理時間は長くなってる><。 アクセスしているブロック数は随分少なくなっているんですが。CPUへの負荷が高いのか(詳細は別途調べるとして)とにかく前より遅くなっちゃNGです。ただ、クエリ自体は随分読みやすい :)

grouping setsで置き換えた今回のクエリ、実行計画を見るとWITH句で書き換えたときのようものにかなり似ていますが、WITH句で書き換えた場合と大きく異なるのは内部で一時表に置き換えられるタイミングにありそうですね。WITH句は本体のクエリとは別で、しかも一番先に実行されますが、以下の実行計画をみると違いますよね。WITH句で書き換えた実行計画と比較するとその違いがよくわかります。

  1  SELECT
2 CASE
3 WHEN quarter IS NULL THEN month
4 ELSE quarter
5 END AS month
6 ,CASE
7 WHEN grouping_id = 1 THEN 'ALL'
8 ELSE shop_code
9 END AS shop_code
10 ,sales_figure
11 FROM (
12 SELECT
13 grouping_id(shop_code) as grouping_id
14 ,quarter
15 ,month
16 ,shop_code
17 ,SUM(sales_figure) AS sales_figure
18 FROM
19 (
20 SELECT
21 CASE
22 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
23 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
24 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
25 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
26 END AS quarter
27 ,SUBSTR(starting_date,1,6) AS month
28 ,shop_code
29 ,sales_figure
30 FROM
31 test2
32 WHERE
33 SUBSTR(starting_date,1,6) BETWEEN '201101' AND '201103'
34 )
35 GROUP BY GROUPING SETS (
36 (month, shop_code),
37 (quarter, shop_code),
38 (month),
39 (quarter)
40 )
41 )
42 WHERE
43 shop_code = '1000'
44 OR grouping_id = 1
45 ORDER BY
46 month
47* ,shop_code

8行が選択されました。

経過: 00:00:34.88

実行計画
----------------------------------------------------------
Plan hash value: 1500989315

------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 711K| 33M| | 93048 (3)| 00:18:37 | | |
| 1 | SORT ORDER BY | | 711K| 33M| 43M| 93048 (3)| 00:18:37 | | |
| 2 | VIEW | | 711K| 33M| | 84288 (3)| 00:16:52 | | |
| 3 | TEMP TABLE TRANSFORMATION | | | | | | | | |
| 4 | LOAD AS SELECT | SYS_TEMP_0FD9D6636_4A53D793 | | | | | | | |
| 5 | PARTITION HASH ALL | | 15M| 351M| | 84280 (3)| 00:16:52 | 1 | 4 |
|* 6 | TABLE ACCESS FULL | TEST2 | 15M| 351M| | 84280 (3)| 00:16:52 | 1 | 4 |
| 7 | LOAD AS SELECT | SYS_TEMP_0FD9D6637_4A53D793 | | | | | | | |
| 8 | SORT GROUP BY ROLLUP | | 1 | 33 | | 3 (34)| 00:00:01 | | |
| 9 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6636_4A53D793 | 1 | 33 | | 2 (0)| 00:00:01 | | |
| 10 | LOAD AS SELECT | SYS_TEMP_0FD9D6637_4A53D793 | | | | | | | |
| 11 | SORT GROUP BY ROLLUP | | 1 | 23 | | 3 (34)| 00:00:01 | | |
| 12 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6636_4A53D793 | 1 | 23 | | 2 (0)| 00:00:01 | | |
| 13 | VIEW | | 1 | 50 | | 2 (0)| 00:00:01 | | |
|* 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6637_4A53D793 | 1 | 50 | | 2 (0)| 00:00:01 | | |
------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

6 - filter(SUBSTR("STARTING_DATE",1,6)<='201103' AND SUBSTR("STARTING_DATE",1,6)>='201101')
14 - filter("SYS_TEMP_0FD9D6637_4A53D793"."C1"='1000' OR
BIN_TO_NUM(SYS_OP_VECBIT(SYS_OP_NUMTORAW("SYS_TEMP_0FD9D6637_4A53D793"."D0"),1))=1)


統計
----------------------------------------------------------
761 recursive calls
52432 db block gets
405879 consistent gets
405750 physical reads
1944 redo size
871 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
8 rows processed

GROUPING SETSで内部的に行われる一時表作成の効率が今ひとつなのであれば、やはり明示的にWITH句で宣言してからやったほうがいいのかな〜ということで、WITH句で一番大きな一時表を作ってみます。

お〜〜〜〜〜〜〜〜〜〜っ。随分変りましたねー。

  1  WITH
2 t01 AS
3 (
4 SELECT
5 SUBSTR(starting_date,1,6) AS month
6 ,CASE
7 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
8 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
9 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
10 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
11 END AS quarter
12 ,shop_code
13 ,SUM(sales_figure) as sales_figure
14 FROM
15 test2
16 WHERE
17 starting_date BETWEEN '20110101' AND '20110331'
18 GROUP BY
19  SUBSTR(starting_date,1,6)
20 ,SUBSTR(starting_date,5,2)
21 ,shop_code
22 )
23 SELECT
24 CASE
25 WHEN quarter IS NULL THEN month
26 ELSE quarter
27 END AS month
28 ,CASE
29 WHEN grouping_id = 1 THEN 'ALL'
30 ELSE shop_code
31 END AS shop_code
32 ,sales_figure
33 FROM (
34 SELECT
35 grouping_id(shop_code) as grouping_id
36 ,quarter
37 ,month
38 ,shop_code
39 ,SUM(sales_figure) AS sales_figure
40 FROM
41 t01
42 GROUP BY GROUPING SETS (
43 (month, shop_code),
44 (quarter, shop_code),
45 (month),
46 (quarter)
47 )
48 )
49 WHERE
50 shop_code = '1000'
51 OR grouping_id = 1
52 ORDER BY
53 month
54* ,shop_code

8行が選択されました。

経過: 00:00:11.83

実行計画
----------------------------------------------------------
Plan hash value: 3348003365

----------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2758 | 134K| 83181 (2)| 00:16:39 | | |
| 1 | SORT ORDER BY | | 2758 | 134K| 83181 (2)| 00:16:39 | | |
| 2 | VIEW | | 2758 | 134K| 83180 (2)| 00:16:39 | | |
| 3 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 4 | LOAD AS SELECT | SYS_TEMP_0FD9D6650_4A53D793 | | | | | | |
| 5 | HASH GROUP BY | | 17550 | 411K| 83172 (2)| 00:16:39 | | |
| 6 | PARTITION HASH ALL | | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 7 | TABLE ACCESS FULL | TEST2 | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
| 8 | LOAD AS SELECT | SYS_TEMP_0FD9D6651_4A53D793 | | | | | | |
| 9 | SORT GROUP BY ROLLUP | | 1 | 33 | 3 (34)| 00:00:01 | | |
| 10 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6650_4A53D793 | 1 | 33 | 2 (0)| 00:00:01 | | |
| 11 | LOAD AS SELECT | SYS_TEMP_0FD9D6651_4A53D793 | | | | | | |
| 12 | SORT GROUP BY ROLLUP | | 1 | 23 | 3 (34)| 00:00:01 | | |
| 13 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6650_4A53D793 | 1 | 23 | 2 (0)| 00:00:01 | | |
| 14 | VIEW | | 1 | 50 | 2 (0)| 00:00:01 | | |
|* 15 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6651_4A53D793 | 1 | 50 | 2 (0)| 00:00:01 | | |
----------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

7 - filter("SYS_TBL_$2$"."STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "SYS_TBL_$2$"."STARTING_DATE">='20110101')
15 - filter("SYS_TEMP_0FD9D66B3_4A54062A"."C1"='1000' OR
BIN_TO_NUM(SYS_OP_VECBIT(SYS_OP_NUMTORAW("SYS_TEMP_0FD9D66B3_4A54062A"."D0"),1))=1)


統計
----------------------------------------------------------
358 recursive calls
53 db block gets
302715 consistent gets
302589 physical reads
1712 redo size
871 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
8 rows processed


じゃ、WITH句だけで行ったチューニング同様に2つの一時表を作ることにしましょー :)
WITH句で2つの一時表を準備(本体の表へは1度だけアクセス)、かつ、grouping sets を使いunion allの利用は最小限にしてみました。w

読みやすくなったし、速度的にも WITH句で2つの一時表を使う場合と変らないし、GROUPING SETSだけだと今回のような問題は解決しづらいかもしれませんね、今のところ。
可読性の向上にはかなり効果あるとおもいます、GROUPING SETSって。


  1  WITH
2 t01 AS
3 (
4 SELECT
5 SUBSTR(starting_date,1,6) AS month
6 ,CASE
7 WHEN SUBSTR(starting_date,5,2) BETWEEN '04' AND '06' THEN 'Q1'
8 WHEN SUBSTR(starting_date,5,2) BETWEEN '07' AND '09' THEN 'Q2'
9 WHEN SUBSTR(starting_date,5,2) BETWEEN '10' AND '12' THEN 'Q3'
10 WHEN SUBSTR(starting_date,5,2) BETWEEN '01' AND '03' THEN 'Q4'
11 END AS quarter
12 ,shop_code
13 ,SUM(sales_figure) as sales_figure
14 FROM
15 test2
16 WHERE
17 starting_date BETWEEN '20110101' AND '20110331'
18 GROUP BY
19  SUBSTR(starting_date,1,6)
20 ,SUBSTR(starting_date,5,2)
21 ,shop_code
22 ),
23 t02 AS
24 (
25 SELECT
26 month
27 ,quarter
28 ,shop_code
29 ,sales_figure
30 FROM
31 t01
32 WHERE
33 month BETWEEN '201101' AND '201103'
34 AND shop_code = '1000'
35 )
36 SELECT
37 CASE
38 WHEN quarter IS NULL THEN month
39 ELSE quarter
40 END AS month
41 ,shop_code
42 ,sales_figure
43 FROM (
44 SELECT
45 quarter
46 ,month
47 ,shop_code
48 ,SUM(sales_figure) AS sales_figure
49 FROM
50 t02
51 GROUP BY GROUPING SETS (
52 (month, shop_code),
53 (quarter, shop_code)
54 )
55 UNION ALL
56 SELECT
57 quarter
58 ,month
59 ,'ALL' AS shop_code
60 ,SUM(sales_figure) AS sales_figure
61 FROM
62 t01
63 GROUP BY GROUPING SETS (
64 (month),
65 (quarter)
66 )
67 )
68 ORDER BY
69 month
70* ,shop_code

8行が選択されました。

経過: 00:00:08.34

実行計画
----------------------------------------------------------
Plan hash value: 1439899557

------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17 | 595 | 83223 (2)| 00:16:39 | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D667D_4A53D793 | | | | | | |
| 3 | HASH GROUP BY | | 17550 | 411K| 83172 (2)| 00:16:39 | | |
| 4 | PARTITION HASH ALL | | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
|* 5 | TABLE ACCESS FULL | TEST2 | 394K| 9242K| 83161 (2)| 00:16:38 | 1 | 4 |
| 6 | SORT ORDER BY | | 17 | 595 | 51 (10)| 00:00:01 | | |
| 7 | VIEW | | 17 | 595 | 50 (8)| 00:00:01 | | |
| 8 | UNION-ALL | | | | | | | |
| 9 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 10 | LOAD AS SELECT | SYS_TEMP_0FD9D667E_4A53D793 | | | | | | |
|* 11 | TABLE ACCESS FULL | SYS_TEMP_0FD9D667D_4A53D793 | 1 | 24 | 17 (0)| 00:00:01 | | |
| 12 | LOAD AS SELECT | SYS_TEMP_0FD9D667F_4A53D793 | | | | | | |
| 13 | HASH GROUP BY | | 1 | 33 | 3 (34)| 00:00:01 | | |
| 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9D667E_4A53D793 | 1 | 33 | 2 (0)| 00:00:01 | | |
| 15 | LOAD AS SELECT | SYS_TEMP_0FD9D667F_4A53D793 | | | | | | |
| 16 | HASH GROUP BY | | 1 | 23 | 3 (34)| 00:00:01 | | |
| 17 | TABLE ACCESS FULL | SYS_TEMP_0FD9D667E_4A53D793 | 1 | 23 | 2 (0)| 00:00:01 | | |
| 18 | VIEW | | 1 | 37 | 2 (0)| 00:00:01 | | |
| 19 | TABLE ACCESS FULL | SYS_TEMP_0FD9D667F_4A53D793 | 1 | 37 | 2 (0)| 00:00:01 | | |
| 20 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 21 | LOAD AS SELECT | SYS_TEMP_0FD9D6680_4A53D793 | | | | | | |
| 22 | TABLE ACCESS FULL | SYS_TEMP_0FD9D667D_4A53D793 | 17550 | 411K| 17 (0)| 00:00:01 | | |
| 23 | LOAD AS SELECT | SYS_TEMP_0FD9D6681_4A53D793 | | | | | | |
| 24 | HASH GROUP BY | | 1 | 27 | 3 (34)| 00:00:01 | | |
| 25 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6680_4A53D793 | 1 | 27 | 2 (0)| 00:00:01 | | |
| 26 | LOAD AS SELECT | SYS_TEMP_0FD9D6681_4A53D793 | | | | | | |
| 27 | HASH GROUP BY | | 1 | 17 | 3 (34)| 00:00:01 | | |
| 28 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6680_4A53D793 | 1 | 17 | 2 (0)| 00:00:01 | | |
| 29 | VIEW | | 1 | 31 | 2 (0)| 00:00:01 | | |
| 30 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6681_4A53D793 | 1 | 31 | 2 (0)| 00:00:01 | | |
------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

5 - filter("STARTING_DATE"<='20110331' AND SUBSTR("STARTING_DATE",1,6)<='201103' AND
SUBSTR("STARTING_DATE",1,6)>='201101' AND "STARTING_DATE">='20110101')
11 - filter("SYS_TBL_$2$"."C0">='201101' AND "SYS_TBL_$2$"."C0"<='201103' AND "SYS_TBL_$2$"."C2"='1000')


統計
----------------------------------------------------------
790 recursive calls
82 db block gets
302843 consistent gets
302593 physical reads
3964 redo size
871 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
8 rows processed

次回は、月毎のレンジパーティションで同じ対処をしたらとこまで改善するのか確認しておきましょう。




これまでのあらずじ…

いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #1
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #2
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #3
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #4
いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #5

|

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/106341/52298108

この記事へのトラックバック一覧です: いろいろと面倒くさい大人の事情縛りのOracleパフォーマンスチューニング #6:

コメント

コメントを書く