在存储过程中重用大型 SQL 查询

Posted

技术标签:

【中文标题】在存储过程中重用大型 SQL 查询【英文标题】:Reusing large SQL queries in stored procedures 【发布时间】:2017-06-09 21:33:33 【问题描述】:

我有许多长 SQL 选择查询(150 行以上),我想在 PL/SQL 包中使用它们。该包具有执行 SQL 查询并将结果插入到单独的表中、将 SQL 结果与另一个表进行比较、删除行等的过程

用以下方式存储 SQL 结果相当容易:

INSERT into TABLE1
SELECT .... (150 line ugly select query goes here)

问题是,我想将选择 SQL 存储在游标/函数/视图/whatever-works 中,因此我不必将 150 行查询粘贴到使用 SQL 的每个过程中。

我可以将 SQL 存储为游标,然后在包过程中循环遍历游标,获取每一行并插入到我的表中。但是考虑到我使用游标的唯一动机是减少我的包的行数,这似乎非常低效。

有没有更好的方法在不同的过程中调用 SQL 选择查询而不复制和粘贴所有 150 行?如果这是一个脚本,我会将 SQL 存储在一个文本文件中,然后只需将文本文件读入一个变量,并在需要时将该变量传递给 sqlplus。但我对 PL/SQL 不是很熟悉。

代码:

CREATE OR REPLACE PACKAGE BODY MyPackage
as
Cursor my_cursor
select (150+ lines goes here)

PROCEDURE PopulateTable
is 
TYPE fetch_array IS TABLE OF my_cursor%ROWTYPE;
s_array fetch_array;
BEGIN

  open my_cursor;

  LOOP
    FETCH tran_cursor BULK COLLECT INTO s_array;
    FORALL counter in 1..s_array.COUNT
        INSERT INTO my_table VALUES s_array(counter);
    EXIT when s_array%NOTFOUND;
  END LOOP;  

  close my_cursor;
  COMMIT;

END PopulateTable;
END MyPackage;

【问题讨论】:

您的视图想法听起来不错,尽管 bulk collectforall 的效率并没有比普通 SQL 低多少。如果交易量很大,也许limit 子句值得考虑。 【参考方案1】:

我不确定这是否是最好的方法,但我想到的是可变光标。你可以使用SYS_REFCURSOR 来做到这一点。您可以构建一个包含您的查询的函数,并返回 ref curosr。在您的所有程序中,您都可以调用该函数。这将节省您在每个过程中编写 150 多行查询。更重要的是,它会将您的程序限制为一个查询副本,因此易于维护。

返回引用光标的函数,可能是这样的:

CREATE OR REPLACE FUNCTION my_ugly_query() 
                           RETURN SYS_REFCURSOR
AS
  my_cursor_ref SYS_REFCURSOR;
BEGIN
  OPEN my_cursor_ref FOR
       SELECT -- 150+ lines of query;
  RETURN my_cursor_ref;
END;

使用方法如下:

CREATE OR REPLACE PACKAGE BODY MyPackage
as
PROCEDURE PopulateTable
IS 
  l_cur_refcur   SYS_REFCURSOR;
  s_array        fetch_array;
BEGIN
  l_cur_refcur := my_ugly_query();

  LOOP
    FETCH tran_cursor BULK COLLECT INTO s_array;
    EXIT when s_array%NOTFOUND;
    FORALL counter in 1..s_array.COUNT
        INSERT INTO my_table VALUES s_array(counter);
  END LOOP;  

  CLOSE my_cursor;
  COMMIT;

END PopulateTable;
END MyPackage;

【讨论】:

【参考方案2】:

在包规范而不是包正文中创建光标。然后,您可以使用 package_name.cursor_name 从任何包过程/函数中引用它

【讨论】:

以上是关于在存储过程中重用大型 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

视图和存储过程

DataBaseMySQL 26 存储过程

存储过程和sql语句有啥区别

sql serve创建存储过程,查询指定学生的学号、姓名、课程名、成绩

MySQL基础_存储过程与函数

mysql 存储过程总结(一)