PostgreSQL Oracle 兼容性之 - DBMS_SQL(存储过程动态SQL中使用绑定变量)

本文涉及的产品
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介:

标签

PostgreSQL , Oracle , pl/sql , 存储过程 , plpgsql , 绑定变量 , DB端绑定变量 , prepare , DEALLOCATE , EXECUTE


背景

Oracle中有一个dbms_sql的包,可以让你在存储过程的动态SQL中使用prepared statement。如果动态SQL的调用频次较高,硬解析可能导致一些性能问题(Oracle硬解析的CPU消耗还是蛮大的)。

https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sql.htm#i1028953

DMBS_SQL里面包含的一些类型

Bulk SQL Types  
  
BFILE_TABLE  
  
BINARY_DOUBLE_TABLE  
  
BLOB_TABLE  
  
CLOB_TABLE  
  
DATE_TABLE  
  
INTERVAL_DAY_TO_SECOND_TABLE  
  
INTERVAL_YEAR_TO_MONTH_TABLE  
  
NUMBER_TABLE  
  
TIME_TABLE  
  
TIME_WITH_TIME_ZONE_TABLE  
  
TIMESTAMP_TABLE  
  
TIMESTAMP_WITH_LTZ_TABLE  
  
UROWID_TABLE  
  
VARCHAR2_TABLE  

其中本文用到的 NUMBER_TABLE

TYPE number_table IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;  

在PostgreSQL的INLINE CODE或者FUNCTION中如何使用动态SQL,如何使用绑定变量,如何使用BULK类型呢?

PostgreSQL INLINE CODE或函数的动态SQL、绑定变量使用

首先借这个链接,解释一下Oracle dbms_sql的使用

https://stackoverflow.com/questions/21335493/can-somebody-explain-dbms-sql-number-table

Better to understand DBMS_SQL itself to some extent, before understanding NUMBER_TABLE.   
( I do this for My Learning!)  
  
NUMBER_TABLE  
is Actually,  
  
TYPE number_table IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;  
So, only numbers are allowed!  
  
FlowChart on How DBMS_SQL Works! :  
  
Your interested area comes in bind variable box  
                        -----------  
--                    | open_cursor |  
--                      -----------  
--                           |  
--                           |  
--                           v  
--                         -----  
--          ------------>| parse |  
--          |              -----  
--          |                |  
--          |                | ---------  
--          |                v          |  
--          |           --------------  |  
--          |-------->| bind_variable | |  
--          |     ^     -------------   |  
--          |     |           |         |  
--          |      -----------|         |  
--          |                 |<--------  
--          |                 v  
--          |               query?---------- yes ---------  
--          |                 |                           |  
--          |                no                           |  
--          |                 |                           |  
--          |                 v                           v  
--          |              -------                  -------------  
--          |----------->| execute |            ->| define_column |  
--          |              -------             |    -------------  
--          |                 |------------    |          |  
--          |                 |            |    ----------|  
--          |                 v            |              v  
--          |           --------------     |           -------  
--          |       ->| variable_value |   |  ------>| execute |  
--          |      |    --------------     | |         -------  
--          |      |          |            | |            |  
--          |       ----------|            | |            |  
--          |                 |            | |            v  
--          |                 |            | |        ----------  
--          |                 |<-----------  |----->| fetch_rows |  
--          |                 |              |        ----------  
--          |                 |              |            |  
--          |                 |              |            v  
--          |                 |              |  -----------------  
--          |                 |              | | column_value    |  
--          |                 |              | | variable_value  |  
--          |                 |              |  -----------------  
--          |                 |              |            |  
--          |                 |<--------------------------  
--          |                 |  
--           -----------------|  
--                            |  
--                            v  
--                       ------------  
--                     | close_cursor |  
--                       ------------  

例子

Example: In a DELETE statement, for example, you could bind in an array in the WHERE clause and have the statement be run for each element in the array:  
  
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;  
/   
  
P.S. Pure rip-off, with some more commments ,from Oracle  

PostgreSQL 服务端绑定变量的用法与之类似

PREPARE,准备DB端绑定变量SQL

EXECUTE,绑定并执行

DEALLOCATE,删除绑定变量

详见:

https://www.postgresql.org/docs/devel/static/sql-prepare.html

因此以上Oracle的代码可以改成如下:

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;  
$$;  

例子

建表

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;
$$;

使用动态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;

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

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;

性能对比

vi test.sql

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

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

1、使用动态SQL,TPS约5.2万。

2、使用函数内绑定变量,TPS约13.4万。

参考

https://www.postgresql.org/docs/devel/static/sql-prepare.html

https://stackoverflow.com/questions/21335493/can-somebody-explain-dbms-sql-number-table

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
7月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS费用价格:MySQL、SQL Server、PostgreSQL和MariaDB引擎收费标准
阿里云RDS数据库支持MySQL、SQL Server、PostgreSQL、MariaDB,多种引擎优惠上线!MySQL倚天版88元/年,SQL Server 2核4G仅299元/年,PostgreSQL 227元/年起。高可用、可弹性伸缩,安全稳定。详情见官网活动页。
1156 152
|
7月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎,提供高性价比、稳定安全的云数据库服务,适用于多种行业与业务场景。
889 156
|
10月前
|
SQL 关系型数据库 MySQL
Go语言数据库编程:使用 `database/sql` 与 MySQL/PostgreSQL
Go语言通过`database/sql`标准库提供统一数据库操作接口,支持MySQL、PostgreSQL等多种数据库。本文介绍了驱动安装、连接数据库、基本增删改查操作、预处理语句、事务处理及错误管理等内容,涵盖实际开发中常用的技巧与注意事项,适合快速掌握Go语言数据库编程基础。
1182 213
|
7月前
|
关系型数据库 分布式数据库 数据库
阿里云数据库收费价格:MySQL、PostgreSQL、SQL Server和MariaDB引擎费用整理
阿里云数据库提供多种类型,包括关系型与NoSQL,主流如PolarDB、RDS MySQL/PostgreSQL、Redis等。价格低至21元/月起,支持按需付费与优惠套餐,适用于各类应用场景。
|
7月前
|
SQL Oracle 关系型数据库
Oracle数据库创建表空间和索引的SQL语法示例
以上SQL语法提供了一种标准方式去组织Oracle数据库内部结构,并且通过合理使用可以显著改善查询速度及整体性能。需要注意,在实际应用过程当中应该根据具体业务需求、系统资源状况以及预期目标去合理规划并调整参数设置以达到最佳效果。
456 8
|
11月前
|
Oracle 关系型数据库 数据库
【赵渝强老师】在PostgreSQL中访问Oracle
本文介绍了如何在PostgreSQL中使用oracle_fdw扩展访问Oracle数据库数据。首先需从Oracle官网下载三个Instance Client安装包并解压,设置Oracle环境变量。接着从GitHub下载oracle_fdw扩展,配置pg_config环境变量后编译安装。之后启动PostgreSQL服务器,在数据库中创建oracle_fdw扩展及外部数据库服务,建立用户映射。最后通过创建外部表实现对Oracle数据的访问。文末附有具体操作步骤与示例代码。
857 6
【赵渝强老师】在PostgreSQL中访问Oracle
|
10月前
|
SQL 关系型数据库 PostgreSQL
CTE vs 子查询:深入拆解PostgreSQL复杂SQL的隐藏性能差异
本文深入探讨了PostgreSQL中CTE(公共表表达式)与子查询的选择对SQL性能的影响。通过分析两者底层机制,揭示CTE的物化特性及子查询的优化融合优势,并结合多场景案例对比执行效率。最终给出决策指南,帮助开发者根据数据量、引用次数和复杂度选择最优方案,同时提供高级优化技巧和版本演进建议,助力SQL性能调优。
1089 1
|
11月前
|
存储 SQL 数据库连接
C#程序调用Sql Server存储过程异常处理:调用存储过程后不返回、不抛异常的解决方案
本文分析了C#程序操作Sql Server数据库时偶发的不返回、不抛异常问题,并提出了解决思路。首先解析了一个执行存储过程的函数`ExecuteProcedure`,其功能是调用存储过程并返回影响行数。针对代码执行被阻塞但无异常的情况,文章总结了可能原因,如死锁、无限循环或网络问题等。随后提供了多种解决方案:1) 增加日志定位问题;2) 使用异步操作提升响应性;3) 设置超时机制避免阻塞;4) 利用线程池分离主线程;5) 通过信号量同步线程;6) 监控数据库连接状态确保可用性。这些方法可有效应对数据库操作中的潜在问题,保障程序稳定性。
771 11
|
12月前
|
SQL Oracle 关系型数据库
解决大小写、保留字与特殊字符问题!Oracle双引号在SQL中的特殊应用
在Oracle数据库开发中,双引号的使用是一个重要但易被忽视的细节。本文全面解析了双引号在SQL中的特殊应用场景,包括解决标识符与保留字冲突、强制保留大小写、支持特殊字符和数字开头标识符等。同时提供了最佳实践建议,帮助开发者规避常见错误,提高代码可维护性和效率。
435 6
|
SQL Oracle 关系型数据库
【YashanDB知识库】共享利用Python脚本解决Oracle的SQL脚本@@用法
【YashanDB知识库】共享利用Python脚本解决Oracle的SQL脚本@@用法

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版
  • 推荐镜像

    更多