バインドピーク

 

(5.6)
drop table tab1;
create table tab1(col1 int not null,col2 int,col3 char(100));


drop procedure proc1;

delimiter //
create procedure proc1()
begin
declare i int;
declare j int;
declare k int;

set i = 1;
set j = 1;
set k = 1;

while i <= 1000000 do
insert into tab1 values(i,j,rpad('A',100,j));
set i = i + 1;
set k = k + 1;
if k > 10000 then
set j = j + 1;
set k = 1;
end if;
end while;
end
//
delimiter ;

call proc1();

alter table tab1 add constraint tab1p primary key (col1);
create index ind12 on tab1(col2);
analyze table tab1;

--特異レコード追加
insert into tab1 values(0, 1000000, 'tokui') ;
analyze table tab1;


1.リテラルの場合

select max(md5(COL3)) from tab1 where col2 >= 1000000 ;
select max(md5(COL3)) from tab1 where col2 >= 0 ;

explain select max(md5(COL3)) from tab1 where col2 >= 1000000 ;
explain select max(md5(COL3)) from tab1 where col2 >= 0 ;

2.バインド変数の場合

PREPARE stmt1 FROM 'select max(md5(COL3)) from tab1 where col2 >= ?';
SET @i = 1000000;
EXECUTE stmt1 USING @i;
DEALLOCATE PREPARE stmt1;

PREPARE stmt1 FROM 'select max(md5(COL3)) from tab1 where col2 >= ?';
SET @i = 0;
EXECUTE stmt1 USING @i;
DEALLOCATE PREPARE stmt1;

PREPARE stmt1 FROM 'explain select max(md5(COL3)) from tab1 where col2 >= ?';
SET @i = 1000000;
EXECUTE stmt1 USING @i;
DEALLOCATE PREPARE stmt1;

PREPARE stmt1 FROM 'explain select max(md5(COL3)) from tab1 where col2 >= ?';
SET @i = 0;
EXECUTE stmt1 USING @i;
DEALLOCATE PREPARE stmt1;


バインド変数の場合もSQL実行ごとに実行計画を生成(バインドピーク)

 

 

(12cR1)

https://www.oracle.com/technetwork/jp/database/articles/shibacho/index-2405696-ja.html


drop table tab1 purge;
create table tab1(col1 int not null,col2 int,col3 char(100));


declare
j integer;
k integer;
begin
j := 1;
k := 1;
for i in 1..1000000 loop
insert into tab1 values(i,j,rpad('A',100,j));
commit;
k := k+1;
if k > 10000 then
j := j+1;
k := 1;
end if;
end loop;
end;
/

create unique index ind11 on tab1(col1);
alter table tab1 add constraint tab1p primary key (col1) using index ind11;
create index ind12 on tab1(col2);
exec dbms_stats.gather_table_stats(ownname => 'TEST', tabname => 'TAB1');

--特異レコード追加
insert into tab1 values(0, 1000000, 'tokui') ;
commit ;

exec dbms_stats.gather_table_stats(ownname => 'TEST', tabname => 'TAB1', method_opt =>'for all columns size 255');

col table_name for a12
col column_name for a12
select table_name, column_name, num_distinct, histogram
from user_tab_col_statistics
where table_name = 'TAB1' and column_name = 'COL2' ;

col column_name for a5
select table_name, column_name, endpoint_number, endpoint_value
from user_tab_histograms
where table_name = 'TAB1' and column_name = 'COL2';


1.リテラルの場合
set timing on


select max(ora_hash(COL3,65535)) from tab1 where col2 >= 1000000 ;

select * from table(dbms_xplan.display_cursor()) ;

select max(ora_hash(COL3,65535)) from tab1 where col2 >= 0 ;

select * from table(dbms_xplan.display_cursor()) ;


2.バインド変数の場合
set timing on

variable col2 number;
execute :col2 := 1000000
select max(ora_hash(COL3,65535)) from tab1 where col2 >= :col2;

select * from table(dbms_xplan.display_cursor()) ;


variable col2 number;
execute :col2 := 0
select max(ora_hash(COL3,65535)) from tab1 where col2 >= :col2;

select * from table(dbms_xplan.display_cursor()) ;



最初に実行されたSQLのバインド変数の値によって実行計画が決まる。(バインドピーク)
不適切な実行計画は2回目のSQL実行により適切な実行計画となる。(Adaptive Cursor Sharing)

 

隠しパラメータ"_optim_peek_user_binds"によりバインドピークを無効化し、
バインド変数に依存しない実行計画に固定化可能

select ksppinm as "Hidden Parameter", ksppstvl as "Value"
from x$ksppi join x$ksppcv using (indx)
where ksppinm = '_optim_peek_user_binds';

alter system set "_optim_peek_user_binds" = false;

 

 

 

 

(13)
https://www.postgresql.org/docs/current/sql-prepare.html


drop table tab1;
create table tab1(col1 int not null,col2 int,col3 char(100));

do $$
declare
j integer;
k integer;
begin
j := 1;
k := 1;
for i in 1..1000000 loop
insert into tab1 values(i,j,rpad('A',100,j::char(3)));
k := k+1;
if k > 10000 then
j := j+1;
k := 1;
end if;
end loop;
end
$$
;

create unique index ind11 on tab1(col1);
alter table tab1 add constraint tab1p primary key using index ind11;
create index ind12 on tab1(col2);
analyze tab1;

--特異レコード追加
insert into tab1 values(0, 1000000, 'tokui') ;
analyze tab1;

select * from pg_stats where tablename = 'tab1';

1.リテラルの場合

\timing on

select max(md5(COL3)) from tab1 where col2 >= 1000000 ;
select max(md5(COL3)) from tab1 where col2 >= 0 ;

explain select max(md5(COL3)) from tab1 where col2 >= 1000000 ;
explain select max(md5(COL3)) from tab1 where col2 >= 0 ;


2.バインド変数の場合


prepare stmt1(int) as select max(md5(COL3)) from tab1 where col2 >= $1;
execute stmt1(1000000);
deallocate stmt1;

prepare stmt1(int) as select max(md5(COL3)) from tab1 where col2 >= $1;
execute stmt1(0);
deallocate stmt1;

prepare stmt1(int) as select max(md5(COL3)) from tab1 where col2 >= $1;
explain execute stmt1(1000000);
deallocate stmt1;

prepare stmt1(int) as select max(md5(COL3)) from tab1 where col2 >= $1;
explain execute stmt1(0);
deallocate stmt1;


(1)plan_cache_mode=auto(デフォルト)の場合、
パラメータのあるプリペアド文に対して、汎用的な計画を使うか独自の計画を使うかを、サーバは自動的に選択します。
最初の5回が独自の計画で実行され、その計画の推定コストの平均が計算される。
それから汎用的な計画が作成され、その推定コストが独自の計画のコストの平均と比較されます。

→今回のケースでは、バインド変数の場合もSQL実行ごとに実行計画を生成(バインドピーク)


(2)plan_cache_mode=force_custom_planの場合、パラメータのあるプリペアド文に対して、独自の計画を使う

→バインド変数の場合もSQL実行ごとに実行計画を生成(バインドピーク)

(3)plan_cache_mode=force_generic_planの場合、パラメータのあるプリペアド文に対して、汎用的な計画を使う

→実行計画は同一でバインドピークは無効となる

 

 

(2019)

drop table tab1;
create table tab1(col1 int not null,col2 int,col3 char(100));

declare @i int;
declare @j int;
declare @k int;

set @i= 1;
set @j= 1;
set @k= 1;

while (@i <= 1000000)
begin
insert into tab1 values (@i,@j,'A' + left(replicate(convert(varchar,@j),100),99));
set @i = @i + 1;
set @k = @k + 1;
if @k > 10000
begin
set @j = @j + 1;
set @k = 1;
end

end
go

alter table tab1 add constraint tab1p primary key (col1);
create index ind12 on tab1(col2);
update statistics tab1;

--特異レコード追加
insert into tab1 values(0, 1000000, 'tokui') ;
update statistics tab1;

DBCC SHOW_STATISTICS (tab1, ind12) WITH HISTOGRAM,NO_INFOMSGS

1.リテラルの場合

set statistics time on
go
set statistics profile on;
go

select max(hashbytes('sha2_256', col3)) from tab1 where col2 >= 1000000 ;
select max(hashbytes('sha2_256', col3)) from tab1 where col2 >= 0 ;


2.バインド変数の場合


set statistics time on
go
set statistics profile on;
go

declare @P1 int
declare @sql nvarchar(max)
declare @param nvarchar(2000)
declare @i int


set @sql = 'select max(hashbytes(''sha2_256'', col3)) from tab1 where col2 >= @i'
set @param = N'@i int'
exec sys.sp_prepare @P1 output, @param, @sql;

set @i = 1000000
exec sys.sp_execute @P1, @i;

set @i = 0
exec sys.sp_execute @P1, @i;

exec sys.sp_unprepare @P1;

-- dbcc freeproccache

→どちらを先に実行しても実行計画は同一でバインドピークは無効と思われる(ドキュメントの記載と異なる)



初回実行時のパラメータを使用して実行計画を作成することを「パラメーター スニッフィング」とよぶ。
デフォルトで有効。無効にすると統計的に最も一般的な値を使用する。
下記が対象
Stored procedures
Queries submitted via sp_executesql
Prepared queries

-- 無効にする場合(データベースレベル)
alter database scoped configuration set parameter_sniffing = OFF;

-- 設定確認
select * from sys.database_scoped_configurations;

-- 無効にする場合(クエリレベル)
OPTIMIZE FOR UNKNOWN クエリ ヒント