PostgreSQL动态SQL(兼容oracle DBMS_SQL)

PostgreSQL动态SQL(兼容oracle DBMS_SQL)


 

oracle中的dbms_sql包可以用来执行动态SQL,让我们在存储过程的动态SQL中使用prepared statement。

oracle中dbms_sql包使用介绍:
在这里插入图片描述
例子:
oracle:

DECLARE  
  stmt VARCHAR2(200);  
  dept_no_array DBMS_SQL.NUMBER_TABLE;  
  c NUMBER;  
  dummy NUMBER;  
begin  
  dept_no_array(1) := 10; dept_no_array(2) := 20; /* Put some values into the array */  
  dept_no_array(3) := 30; dept_no_array(4) := 40;  
  dept_no_array(5) := 30; dept_no_array(6) := 40;  
  stmt := 'delete from emp where deptno = :dept_array'; /* A Dynamic SQL String with a bind variable */  
  c := DBMS_SQL.OPEN_CURSOR; /* Open a Cursor! */  
  DBMS_SQL.PARSE(c, stmt, DBMS_SQL.NATIVE); /* Parse the Dynamic SQL , making it happen on the native database to which is connected! */  
  
  DBMS_SQL.BIND_ARRAY(c, ':dept_array', dept_no_array, 1, 4);  
  /* Bind only elements 1 through 4 to the cursor Happens 4 times */  
  
  dummy := DBMS_SQL.EXECUTE(c);  
  /* Execute the Query, and return number of rows deleted! */  
  
  DBMS_SQL.CLOSE_CURSOR(c);   
  
  EXCEPTION WHEN OTHERS THEN  
    IF DBMS_SQL.IS_OPEN(c) THEN  
      DBMS_SQL.CLOSE_CURSOR(c);  
    END IF;  
    RAISE;  
END;  
/  

pg中可以使用服务端绑定变量的方法来实现类似的功能,大致步骤为:
1、PREPARE,准备DB端绑定变量SQL
2、EXECUTE,绑定并执行
3、DEALLOCATE,删除绑定变量

因此可以将上述代码改为:

do language plpgsql $$  
DECLARE  
  stmt VARCHAR(200);  
  dept_no_array numeric[];  
  c numeric;  
begin  
  dept_no_array[1] := 10; dept_no_array[2] := 20; /* Put some values into the array */  
  dept_no_array[3] := 30; dept_no_array[4] := 40;  
  dept_no_array[5] := 30; dept_no_array[6] := 40;  
  execute format('prepare stmt(numeric) as delete from emp where deptno = $1');    /* A Dynamic SQL String with a bind variable */  
  
  foreach c in array dept_no_array[1:4]  
  loop  
    execute format('execute stmt(%s)', c);   -- 执行绑定SQL  
  end loop;  
  
  DEALLOCATE stmt;  
  
  EXCEPTION WHEN OTHERS THEN  
    DEALLOCATE stmt;  
    RAISE;  
END;  
$$;  

 

如果不需要使用绑定变量的话:

do language plpgsql $$  
DECLARE  
  dept_no_array numeric[];  
  c numeric;  
begin  
  dept_no_array[1] := 10; dept_no_array[2] := 20; /* Put some values into the array */  
  dept_no_array[3] := 30; dept_no_array[4] := 40;  
  dept_no_array[5] := 30; dept_no_array[6] := 40;  
  
  foreach c in array dept_no_array[1:4]  
  loop  
    delete from emp where deptno = c;  
  end loop;  
  
  DEALLOCATE stmt;  
  
  EXCEPTION WHEN OTHERS THEN  
    DEALLOCATE stmt;  
    RAISE;  
END;  
$$;  

在pg中动态SQL和绑定变量这两种方式性能又如何呢?

1、建表

do language plpgsql $$
declare
begin
  execute 'drop table if exists test';
  execute 'create table test(id int primary key, info text, crt_time timestamp)';
  
  for i in 0..1023 loop
    execute format('drop table if exists test%s', i);
    execute format('create table test%s (like test including all)', i);
  end loop;
end;
$$;

2、使用动态SQL,写入目标子表

create or replace function dyn_pre(int) returns void as $$
declare
  suffix int := mod($1,1024);
begin
  execute format('insert into test%s values(%s, md5(random()::text), now()) on conflict(id) do update set info=excluded.info,crt_time=excluded.crt_time', suffix, $1);
end;
$$ language plpgsql strict;

3、使用绑定变量,写入目标子表

create or replace function dyn_pre(int) returns void as $$
declare
  suffix int := mod($1,1024);
begin
  execute format('execute p%s(%s)', suffix, $1);
  exception when others then
    execute format('prepare p%s(int) as insert into test%s values($1, md5(random()::text), now()) on conflict(id) do update set info=excluded.info,crt_time=excluded.crt_time', suffix, suffix);
    execute format('execute p%s(%s)', suffix, $1);
end;
$$ language plpgsql strict;

4、性能测试
vi test.sql

\set id random(1,1000000000)
select dyn_pre(:id);

pgbench -M prepared -n -r -P 1 -f ./test.sql -c 16 -j 16 -T 60

动态SQL:

transaction type: ./test.sql
scaling factor: 1
query mode: prepared
number of clients: 16
number of threads: 16
duration: 60 s
number of transactions actually processed: 2543070
latency average = 0.377 ms
latency stddev = 0.254 ms
tps = 42376.154026 (including connections establishing)
tps = 42380.640279 (excluding connections establishing)
statement latencies in milliseconds:
         0.001  \set id random(1,1000000000)
         0.376  select dyn_pre(:id);

绑定变量:

transaction type: ./test.sql
scaling factor: 1
query mode: prepared
number of clients: 16
number of threads: 16
duration: 60 s
number of transactions actually processed: 3105687
latency average = 0.308 ms
latency stddev = 0.399 ms
tps = 51747.630800 (including connections establishing)
tps = 51759.435224 (excluding connections establishing)
statement latencies in milliseconds:
         0.001  \set id random(1,1000000000)
         0.308  select dyn_pre(:id);

1、使用动态SQL,TPS约4.2万。
2、使用函数内绑定变量,TPS约5.1万。
实验使用的是虚拟机,物理机上绑定变量性能差异更明显。

参考链接:
https://docs.oracle.com/database/121/TTPLP/d_sql.htm#TTPLP71257
https://www.postgresql.org/docs/devel/static/sql-prepare.html

posted @ 2023-01-04 03:29  耀阳居士  阅读(421)  评论(0编辑  收藏  举报