注册
达梦FORALL语句使用一例
培训园地/ 文章详情 /

达梦FORALL语句使用一例

mongo-刘太泽 2023/04/06 1653 0 0

1、 前言
在看完这个示例后,你将会对达梦数据库的批量SQL使用有一定的子解。FORALL语句会从达梦SQL引擎向SQL引擎批量发送INSERT、UPDATE、或者DELETE语句,而不是每次发送一条语句。例如,考虑下面的数值型FOR循环会10次迭代执行INSERT语句:
FOR I IN 1 .. 10 LOOP
INSERT INTO table_name
values(...);
END LOOP;
该INSERT语句会从达梦SQL引擎发送到SQL引擎10次。也就是说,会发生10次上下文切换。如果使用FORALL语句替换这个FOR循环,只需要发送一次INSERT语句,但是会执行10次。在这种情况下,在达梦SQL和SQL之间只会发生一次上下文切换。
FORALL语句具有如下结构(方括号中的保留字是可选的):
FORALL loop_counter IN bounds_clause
SQL_STATMENT [SAVE EXCEPTIONS];

其中,bounds_clause是下面形式之一:
Lower_limit .. upper_limit
INDICE OF collection_name BETWEEN lower_limit .. upper_limit
VALUES OF collection_name
该FORALL语句有个与之相关的隐式定义的循环计数器变量。Bounds_clause控制循环计数器变量的值,以及循环迭代的次数,有3种形式。第一种形式指明循环计数器的上限和下限。这个语法非常类似于数值型FOR循环。第二种形式INDICES OF …会引用特定集合中单个元素的下标。这个集合也许是个嵌套表,或者具有数字下标的联合数组。
Bounds_clause的第三种形式VALUES OF …,会引用特定集合中单个元素的值,这个集合可能是嵌套表或者联合数组。
接下来,SQL_STATEMENT是引用一个或者多个集合的静态或者动态的INSERT、UPDATE、或者DELETE语句。最后,即使当SQL_STATEMENT导致异常时,选项SAVE EXCEPTIONS仍旧能够保证FORALL语句的继续执行。
2、 FORALL示例
本示例演示FORALL语句的使用方式。这个范例使用一个TEST表,可以很容易地插入、更新或者删除TEST表中的数据行,而不会影响正式环境中的模式,也不会违反任何完整性约束。
CREATE TABLE TEST(ROW_NUM NUMBER,ROW_TEXT VARCHAR(10));
DECLARE
--Define collection types and variables
TYPE row_num_type IS TABLE OF NUMBER INDEX BY INTEGER;
TYPE row_text_type IS TABLE OF VARCHAR(10) INDEX BY INTEGER;

	row_num_tab row_num_type;
	row_text_tab row_text_type;
	v_total NUMBER;

BEGIN
--Populate collections
FOR i IN 1 .. 10 LOOP
row_num_tab(i) := i;
row_text_tab(i):='row'||i;
END LOOP;
--Populate TEST table
FORALL i IN 1 .. 10
INSERT INTO TEST(ROW_NUM,ROW_TEXT)
VALUES (row_num_tab(i),row_text_tab(i));
COMMIT;
--Check how many rows were inserted in the TEST table
--and display it on the screen.
SELECT COUNT(*)
INTO v_total
FROM TEST;
DBMS_OUTPUT.PUT_LINE
('There are '||v_total||' rows in the TEST table');
END;
正如前面所提到的那样,当SQL语句与FORALL语句一起使用时,它们会引用集合元素,所以,在这个脚本中,定义两个集合类型作为联合数组,以及定义两个集合(使用数值型FOR循环进行填充)。接下来,使用来自于两个集合的数据来填充TEST表。
当运行以上脚本时,会产生如下输出:
There are 10 rows in the TEST table
以下这个示例测试使用FORALL语句所带来的性能改进效果。在本例中,会比较使用不同的方法对TEST表执行INSERT语句所花费的时间。第一次10000次插入操作使用数值型FOR循环,第二次10000次插入操作使用FORALL语句。
DECLARE
--Define collection types and variables
TYPE row_num_type IS TABLE OF NUMBER INDEX BY INTEGER;
TYPE row_text_type IS TABLE OF VARCHAR(10) INDEX BY INTEGER;

	row_num_tab row_num_type;
	row_text_tab row_text_type;
	v_total NUMBER;
	v_start_time INTEGER;
	v_end_time INTEGER;

BEGIN
--Populate collections
FOR i IN 1 .. 10000 LOOP
row_num_tab(i) := i;
row_text_tab(i) := 'row' ||i;
END LOOP;
--Record start time
v_start_time := DBMS_UTILITY.GET_TIME;

	--Insert first 10000 rows
	FOR i in 1 .. 10000 LOOP
		INSERT INTO TEST(ROW_NUM,TEST.ROW_TEXT)
		VALUES(row_num_tab(i),row_text_tab(i));
	END LOOP;
	
	--Record end time
	v_end_time := DBMS_UTILITY.GET_TIME;
	
	--Calculate and display elapsed time
	DBMS_OUTPUT.PUT_LINE('Duration of the FOR LOOP: '||(v_end_time-v_start_time));
	-- Record the start time
	v_start_time := DBMS_UTILITY.GET_TIME;
	
	--Insert second 100 rows
	FORALL i IN 1 .. 10000
		INSERT INTO TEST(ROW_NUM,ROW_TEXT)
		VALUES(row_num_tab(i),row_text_tab(i));
	-- Record the end time
	v_end_time := DBMS_UTILITY.GET_TIME;
	
	--Calculate and display elapsed time
	DBMS_OUTPUT.PUT_LINE('Duration of the FORALL statement: '||(v_end_time-v_start_time));
	COMMIT;
END;

为计算FOR循环和FORALL语句的执行时间,需要使用DBMS_UTILITY包的GET_TIME函数,达梦数据库的SYS拥有这个包。
GET_TIME函数返回当前时间,精度为百分之一秒。上述示例的一次输出如下所示:
Duration of the FOR LOOP: 7
Duration of the FORALL statement: 9
经过多次运行比较,在达梦数据库系统中,批SQL引擎中FORALL在性能上并没有FOR语句有明显的改进,期待后续版本对此有较大的改进。
3、 结论
达梦SQL引擎在批语句FORALL方面在性能上没有明显改善,目前的V8版本使用FOR语句和FORALL语句均可,期待后续版本中达梦SQL引擎在批语句方面有较大的改进。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服