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