PL/SQL过程。避免SQL重复

PL/SQL过程。避免SQL重复,sql,oracle,plsql,Sql,Oracle,Plsql,我使用的是Oracle数据库,有几个大型SQL查询(150行以上)。我需要每次运行这些倍数来执行不同的测试。为了尽量减少代码重复,我通常会使用with子句。例如,如果我想检查两个查询的结果是否相同,并确认计数为非零,我可能会使用 WITH STATEMENT_1 AS ( SELECT ... ) , STATEMENT_2 AS ( SELECT ... ) SELECT COUNT(*) FROM STATEMENT_1 UNION ALL SELECT COUNT(*) FROM

我使用的是Oracle数据库,有几个大型SQL查询(150行以上)。我需要每次运行这些倍数来执行不同的测试。为了尽量减少代码重复,我通常会使用with子句。例如,如果我想检查两个查询的结果是否相同,并确认计数为非零,我可能会使用

WITH STATEMENT_1 AS
(
  SELECT ...
)
, STATEMENT_2 AS
(
  SELECT ...
)
SELECT COUNT(*) FROM STATEMENT_1
UNION ALL
SELECT COUNT(*) FROM STATEMENT_2
UNION ALL
(
  SELECT COUNT(*) FROM
  (
    SELECT * FROM STATEMENT_1
    MINUS
    SELECT * FROM STATEMENT_2
  )
);
这很有效。然而,我现在正试图创建一个PL/SQL包来存储和自动销毁这些查询。我正在寻找一种避免SQL代码重复的方法。我见过这样的答案

但是我不想使用游标,因为查询都是基于集合的


总而言之:正在寻找一种方法,在PL/SQL包中封装大型、重复的SQL基查询,而不使用游标

视图是答案的一部分。它仍然不允许你“写一次,到处使用”

您需要一个通用的“程序”(查询、函数或过程),您可以向其提供两个查询(存储查询的名称),并输出所需的信息

我将在下面展示如何在一个过程中实现这一点。您使用两个视图名调用该过程,它将打印一条消息,显示计数。在一个严肃的实现中,您不会使用
DBMS\u输出;我只是展示了如何通过一个过程来实现您的概念,您可以修改它的行为以写入文件,或者您可以将其更改为一个函数以生成一个包含计数的表,等等

注意,由于该过程使用动态SQL,并且表/视图名称不能作为绑定变量传递,所以必须考虑SQL注入(一个坏的东西)。为了解决这个问题,我将视图名称包装在

DBMS\u ASSERT.SIMPLE\u SQL\u NAME
中。因此,确保您的视图具有简单的名称(在技术意义上)

一般程序

create or replace procedure
        comp_queries ( view_name_1 in varchar2, view_name_2 in varchar2 )
is
  cnt_1    number;
  cnt_2    number;
  cnt_diff number;
begin
  execute immediate 'select count(*) from ' || dbms_assert.simple_sql_name(view_name_1)
    into  cnt_1;
  execute immediate 'select count(*) from ' || dbms_assert.simple_sql_name(view_name_2)
    into cnt_2;
  execute immediate 'select count(*) from (
                        select * from ' || view_name_1 || ' minus 
                        select * from ' || view_name_2 || ')'
    into cnt_diff;
  dbms_output.put_line('Count from ' || upper(view_name_1) || ': ' || cnt_1);
  dbms_output.put_line('Count from ' || upper(view_name_2) || ': ' || cnt_2);
  dbms_output.put_line('Count from ' || upper(view_name_1) || ' MINUS '
                             || upper(view_name_2) || ': ' || cnt_diff);
end;
/
编译并确认它可以正常工作

然后通过创建两个视图来测试它。在这里,我编写了两个应该给出相同输出的查询:

create view v_deptno_1 as
  select distinct deptno from emp;

create view v_deptno_2 as
  select deptno from emp group by deptno;
那么,让我们检查一下它们确实产生了相同的输出。(请注意,这比完整的正式证明查询是等效的要弱;这只是您已经在做的事情,检查它们在基础表中当前的数据上是否等效。)

首先,由于我们使用的是
DBMS\u输出
,因此需要打开服务器输出。然后我调用该过程两次——一次调用我们刚刚创建的两个视图名称,一次调用模拟SQL注入攻击的方法(也要始终测试!)


如何将这些SQL转换为视图?视图是封装查询的最传统方式。这能满足您的需要吗?为什么不直接将结果插入带有Id或日期的表中,然后在函数调用后读取结果?什么代码重复?我不明白你想避免什么。@Gordon Linoff-例如,如果我有一个150行的SQL查询,我需要查找计数,但后来需要在减号语句中使用相同的查询,我不想重复完整的150行查询。太好了,谢谢。最后,景色有点慢。所以我选择设置一些空表结构。然后,当我运行mathguy的PLSQL块时,我调用另一个填充表的存储过程,然后我可以运行检查、截断表并继续下一个查询。总而言之,这让我避免了代码重复,并使运行时间保持在合理的速度。谢谢你的帮助。
create view v_deptno_1 as
  select distinct deptno from emp;

create view v_deptno_2 as
  select deptno from emp group by deptno;
SQL> set serveroutput on

SQL> exec comp_queries('v_deptno_1', 'v_deptno_2')

Count from V_DEPTNO_1: 3
Count from V_DEPTNO_2: 3
Count from V_DEPTNO_1 MINUS V_DEPTNO_2: 0

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00

-- SIMULATE SQL INJECTION ATTACK:

SQL> exec comp_queries('v_deptno_1', 'v_deptno_2;delete * from emp')

BEGIN comp_queries('v_deptno_1', 'v_deptno_2;delete * from emp'); END;

*
ERROR at line 1:
ORA-44003: invalid SQL name
ORA-06512: at "SYS.DBMS_ASSERT", line 206
ORA-06512: at "INTRO.COMP_QUERIES", line 10
ORA-06512: at line 1

Elapsed: 00:00:00.01