当前位置:网站首页>Db2 SQL PL的动态SQL
Db2 SQL PL的动态SQL
2022-06-10 18:01:00 【蓝黑2020】
环境
- 操作系统:Ubuntu 20.04
- Db2:11.5.0.0
背景
systools.admin_task_list 是一个administrative view,记录了Db2任务调度的信息。但是 systools.admin_task_list 默认并不存在,只有在创建第一个任务的时候,才会被创建。现在想要删除命名为 abc 开头的任务,该怎么做呢?
这个问题折腾了我好久,后来我意识到,使用静态SQL无法解决问题,最终使用动态SQL找到了解决办法。
我们先来了解一下静态SQL和动态SQL。注意,本文内容是我个人的经验总结,有些术语或者概念可能有理解不到位甚至错误的地方,还需继续深入研究。
复合语句无法运行静态DDL
创建文件 test1.sql 如下:
begin
create table db2inst1.t1 (c1 int, c2 int);
end@
无论表 t1 是否已存在,运行脚本 test1.sql ,如下:
* temp0531 db2 -[email protected] -f test1.sql
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0104N An unexpected token "<ddl-statement>" was found following "".
Expected tokens may include: "". LINE NUMBER=2. SQLSTATE=42601
这说明在复合语句里无法直接运行DDL语句。
注:
select * from t1不行,必须使用它的变种,比如select count(*) into n from t1,或者set n = (select count(*) from t1);delete from t1可以,但如果t1为空,则会报一个警告信息;
复合语句运行动态DDL
要解决这个问题,一个办法是改用动态SQL。创建文件 test2.sql 如下:
begin
declare n int default 0;
declare stmt varchar(1000);
set n = (select count(*) from syscat.tables
where tabschema = 'DB2INST1' and tabname = 'T1');
if (n = 0) then
set stmt = 'create table db2inst1.t1 (c1 int, c2 int)';
execute immediate stmt;
end if;
end@
无论表 t1 是否已存在,运行脚本 test2.sql ,如下:
* temp0531 db2 -[email protected] -f test2.sql
DB20000I The SQL command completed successfully.
该脚本使用了动态SQL,同时加上了判断,只有在表 t1 不存在的时候,才会创建它。
注: execute immediate 语句无法运行 select 语句。
存储过程运行静态DDL
创建文件 test3.sql 如下:
create or replace procedure myproc()
begin
declare n int default 0;
set n = (select count(*) from syscat.tables
where tabschema = 'DB2INST1' and tabname = 'T1');
if (n = 0) then
create table db2inst1.t1 (c1 int, c2 int);
end if;
end@
注意,该脚本只是创建存储过程,并没有真正运行其中的逻辑。
运行脚本 test3.sql ,如果表 t1 不存在,没有问题,但是如果表 t1 已存在,则会报错:
* temp0531 db2 -[email protected] -f test3.sql
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0601N The name of the object to be created is identical to the existing
name "DB2INST1.T1" of type "TABLE". LINE NUMBER=9. SQLSTATE=42710
这就奇怪了,明明前面是做了判断,只有这个表不存在的时候才会创建它,但为什么还是会报错说表已存在呢?更何况,这个脚本只是创建存储过程,并没有真正运行其中的逻辑呢。
其中的大致原因,是因为这样的SQL是“静态SQL”,也就是说在创建存储过程的时候,就要先编译好其中create语句,所以即使有 if 语句做了判断,也无济于事。
存储过程运行动态DDL
创建文件 test4.sql 如下:
create or replace procedure myproc()
begin
declare n int default 0;
declare stmt varchar(1000);
set n = (select count(*) from syscat.tables
where tabschema = 'DB2INST1' and tabname = 'T1');
if (n = 0) then
set stmt = 'create table db2inst1.t1 (c1 int, c2 int)';
execute immediate stmt;
end if;
end@
无论表 t1 是否已存在,运行脚本 test2.sql ,如下:
* temp0531 db2 -[email protected] -f test4.sql
DB20000I The SQL command completed successfully.
因为加了判断,所以即使多次调用该存储过程也不会有问题:
* temp0531 db2 "call myproc"
Return Status = 0
问题
但是有一个问题我还是没有解决。
创建文件 test5.sql 如下:
set serveroutput on@
begin
declare n, m int default 0;
set n = (select count(*) from syscat.tables
where tabschema = 'DB2INST1' and tabname = 'T1');
if (n = 1) then
call dbms_output.put_line('OK');
set m = (select count(*) from db2inst1.t1);
else
call dbms_output.put_line('oh no');
set m = 0;
end if;
call dbms_output.put_line('m = ' || m);
end@
set serveroutput off@
代码逻辑很简单,如果表 db2inst1.t1 存在,就输出其行数,否则就输出0。
运行脚本 test5.sql ,假如表 db2inst1.t1 存在,则没有问题,结果如下:
* temp0531 db2 -[email protected] -f test5.sql
DB20000I The SET SERVEROUTPUT command completed successfully.
DB20000I The SQL command completed successfully.
OK
m = 2
DB20000I The SET SERVEROUTPUT command completed successfully.
但是如果表 db2inst1.t1 不存在,则报错如下:
* temp0531 db2 -[email protected] -f test5.sql
DB20000I The SET SERVEROUTPUT command completed successfully.
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0204N "DB2INST1.T1" is an undefined name. LINE NUMBER=9. SQLSTATE=42704
DB20000I The SET SERVEROUTPUT command completed successfully.
如前面所说,因为这样的SQL是“静态SQL”,也就是说在运行前就要先编译好package,所以即使有 if 语句做了判断,也无济于事。
解决办法是改用“动态SQL”,但是 execute immediate 语句无法运行 select 语句。这个问题该如何解决呢?
后来我找到了一个曲线救国的方法,就是 execute immediate + 复合语句。
修改 test5.sql ,创建文件 test6.sql 如下:
set serveroutput on@
begin
declare n, m int default 0;
declare stmt varchar(1000);
set n = (select count(*) from syscat.tables
where tabschema = 'DB2INST1' and tabname = 'T1');
if (n = 1) then
call dbms_output.put_line('OK');
set stmt = 'begin declare m int;'
|| ' set m = (select count(*) from db2inst1.t1);'
|| ' call dbms_output.put_line(''m = '' || m);'
|| ' end';
execute immediate stmt;
else
call dbms_output.put_line('oh no');
set m = 0;
call dbms_output.put_line('m = ' || m);
end if;
end@
set serveroutput off@
运行脚本 test6.sql ,假如表 t1 存在,如下:
* temp0531 db2 -[email protected] -f test6.sql
DB20000I The SET SERVEROUTPUT command completed successfully.
DB20000I The SQL command completed successfully.
OK
m = 2
DB20000I The SET SERVEROUTPUT command completed successfully.
假如表 t1 不存在,如下:
* temp0531 db2 -[email protected] -f test6.sql
DB20000I The SET SERVEROUTPUT command completed successfully.
DB20000I The SQL command completed successfully.
oh no
m = 0
DB20000I The SET SERVEROUTPUT command completed successfully.
真不容易!终于有个办法了,虽然丑了点。
示例
现在来看本文开头所提出的实际问题。
systools.admin_task_list 是一个administrative view,记录了Db2任务调度的信息。但是 systools.admin_task_list 默认并不存在,只有在创建第一个任务的时候,才会被创建。现在想要删除命名为 abc 开头的任务,该怎么做呢?
要想知道哪些任务以 abc 开头,就要查询 systools.admin_task_list ,但是它不一定存在。所以,显然先要查询它是否存在,如果存在,再以动态select的方式来访问它。
创建文件 test7.sql 如下:
set serveroutput on@
create or replace type strarray as varchar(255) array[]@
begin
declare n int default 0;
declare stmt varchar(1000);
set n = (select count(*) from syscat.tables
where tabschema = 'SYSTOOLS' and tabname = 'ADMIN_TASK_LIST');
if (n = 1) then
call dbms_output.put_line('OK');
set stmt = 'begin declare m, i int;'
|| ' declare arr1 strarray;'
|| ' declare stmt2 varchar(1000);'
|| ' set arr1 = array[select name from systools.admin_task_list where name like ''abc%''];'
|| ' set m = cardinality(arr1);'
|| ' set i = 1;'
|| ' while (i <= m) do'
|| ' call dbms_output.put_line(''haha'' || arr1[i]);'
|| ' set stmt2 = ''call sysproc.admin_task_remove('''''' || arr1[i] || '''''', null)'';'
|| ' execute immediate stmt2;'
|| ' set i = i + 1;'
|| ' end while;'
|| ' call dbms_output.put_line(''m = '' || m);'
|| ' end';
call dbms_output.put_line('stmt = ' || stmt);
execute immediate stmt;
else
call dbms_output.put_line('SYSTOOLS.ADMIN_TASK_LIST does not exist.');
end if;
end@
set serveroutput off@
注意代码中的六个单引号!这个问题又折腾了我好长时间,这是因为在 execute immediate 里又嵌套了一层 execute immediate ,也就是说一共有3层。
- 在内层,只需1个引号 (
call sysproc.admin_task_remove('xxx', null)); - 在中间层,因为文本本身是用引号引起来的,所以需要3个引号(
set stmt2 = 'call sysproc.admin_task_remove(''' || xxx || ''', null)'); - 在外层,要把中间层代码变成文本,所以每个引号都变成两个引号(
set stmt = 'set stmt2 = ''call sysproc.admin_task_remove('''''' || xxx || '''''', null)''');
运行脚本 test7.sql ,假如 systools.admin_task_list 不存在,如下:
* temp0531 db2 -[email protected] -f test7.sql
DB20000I The SET SERVEROUTPUT command completed successfully.
DB20000I The SQL command completed successfully.
DB20000I The SQL command completed successfully.
SYSTOOLS.ADMIN_TASK_LIST does not exist.
DB20000I The SET SERVEROUTPUT command completed successfully.
创建task:
CALL SYSPROC.ADMIN_TASK_ADD
('abcaaaaa',
NULL,
NULL,
NULL,
'0 0-23 * * *',
'PROD',
'FLUSH_EVENT_MONITOR',
NULL,
NULL,
NULL )
类似的,再建两个task,命名为 abcbbbbb 和 kkkkk 。
现在, systools.admin_task_list 就被Db2自动创建出来了:
* temp0531 db2 "select name from systools.admin_task_list"
NAME ---------
abcaaaaa
abcbbbbb
kkkkk
3 record(s) selected.
再次运行脚本 test7.sql ,如下:
* temp0531 db2 -[email protected] -f test7.sql
DB20000I The SET SERVEROUTPUT command completed successfully.
DB20000I The SQL command completed successfully.
DB20000I The SQL command completed successfully.
OK
stmt = begin declare m, i int; declare arr1 strarray; declare stmt2 varchar(1000); set arr1 = array[select name from systools.admin_task_list where name like 'abc%']; set m = cardinality(arr1); set i = 1; while (i <= m) do call dbms_output.put_line('haha' || arr1[i]); set stmt2 = 'call sysproc.admin_task_remove(''' || arr1[i] || ''', null)'; execute immediate stmt2; set i = i + 1; end while; call dbms_output.put_line('m = ' || m); end
hahaabcaaaaa
hahaabcbbbbb
m = 2
DB20000I The SET SERVEROUTPUT command completed successfully.
再次查看 systools.admin_task_list :
* temp0531 db2 "select name from systools.admin_task_list"
NAME -------
kkkkk
1 record(s) selected.
可见, abc 开头的task都被删除了。
总结
大致总结如下(可能会有疏漏或者错误,待查):
- 复合语句无法运行静态DDL;
- 复合语句可以运行静态DML,但是要确保表存在(无论是否有if判断);
- 复合语句可以运行动态DDL;
- 复合语句可以运行动态DML,但select语句不行;
- 存储过程可以运行静态DDL/DML,但是在创建存储过程时:
- 若有
create语句,要确保表不存在; - 若有
select语句,要确保表存在; - 若有
drop语句,不受影响,表存在不存在都OK;
- 若有
- 存储过程可以运行动态DDL;
- 存储过程可以运行动态DML,但select语句不行;
要想动态运行select语句,可以采用 execute immediate 加复合语句的方法,曲线救国 。
边栏推荐
- “数字化转型,数据先行”,谈谈数据治理对企业来说到底有多重要
- 位运算题目
- 阅读micropyton源码-添加C扩展类模块(2)
- 国货彩妆,败走618
- 关于YUV格式的一些总结
- 商业智能BI如何帮企业降低人力、时间和管理成本?
- [Interface tutorial] how does easycvr set platform cascading through the interface?
- TSMC liudeyin: do not worry about semiconductor inventory correction and cooperation between the United States, Japan and South Korea. This year's performance will increase by 30%!
- 单调栈结构
- c语言---11 分支语句if else
猜你喜欢

使用DAP-Link单独下载可执行文件到MM32F5微控制器

c语言---13 循环语句while

跨域报错:When allowCredentials is true, allowedOrigins cannot contain the special value “*“

NaturalSpeech模型合成语音在CMOS测试中首次达到真人语音水平

c语言---11 分支语句if else

Developers changing the world - Yao Guang teenagers playing Tetris

3. Golang并发入门
![[CEPH] CEPH configuration source code analysis | common/config*](/img/d6/ba8d3ccc28d11e7fd9662b730fd998.jpg)
[CEPH] CEPH configuration source code analysis | common/config*

Form form of the uniapp uview framework, input the verification mobile number and verification micro signal

低碳数据中心建设思路及未来趋势
随机推荐
用基础比率重写清晰的贝叶斯公式
Stream流的常用方法-Lambder
c语言学习回顾---1 基础知识回顾
Huawei cloud Kunpeng devkit code migration practice
数据可视化入门学习,要警惕陷阱误区,把握关键节点
AFL fuzzy multithreading
AD18器件库导入简介
企业数据质量管理:如何进行数据质量评估?
企业管理者的质疑,这么多年的信息化,我们的钱花哪去了?
跨域报错:When allowCredentials is true, allowedOrigins cannot contain the special value “*“
XML & XPath parsing
Set up an online help center to easily help customers solve problems
In the digital era, how can enterprises manage data security and ensure the security of data assets
ACL2022 | bert2BERT:参数复用的高效预训练方法,显著降低超大模型的训练成本
Building smart community Internet of things based on smart road lamp posts
Win7系统下无法正常安装JLINK CDC UART驱动的问题解决
c语言---11 分支语句if else
换根呀呀啊呀
Group a large number of files in the same folder into multiple groups according to settings
[kuangbin]专题二十二 区间DP