首先,我的背景是在SQL Server中。使用CTE(公用表表达式)是轻而易举的,并将其转换为带变量的存储过程,除了用变量名替换输入的值之外,不需要对SQL结构进行任何更改。
然而,在Oracle PL / SQL中,这是一个完全不同的问题。我的CTE作为直接SQL工作正常,但是一旦我尝试将它们包装为PL / SQL,我遇到了许多问题。根据我的理解,SELECT现在需要一个INTO,它只能保存单个记录的结果。但是,我想要多个值的整个记录集。
如果我在这里错过了显而易见的事,我道歉。我认为99%的问题是我需要做出的范式转换。
给出以下示例:
注意:我在这里简化了SQL。我知道下面的例子可以在一个SQL语句中完成。实际的SQL要复杂得多。这是我在这里寻找的基本原则。
WITH A as (SELECT * FROM EMPLOYEES WHERE DEPARTMENT = 200),
B as (SELECT * FROM A WHERE EMPLOYEE_START_DATE > date '2014-02-01'),
C as (SELECT * FROM B WHERE EMPLOYEE_TYPE = 'SALARY')
SELECT 'COUNTS' as Total,
(SELECT COUNT(*) FROM A) as 'DEPT_TOTAL',
(SELECT COUNT(*) FROM B) as 'NEW_EMPLOYEES',
(SELECT COUNT(*) FROM C) as 'NEW_SALARIED'
FROM A
WHERE rowcount = 1;
现在,如果我想使用传入或预定义在顶部的变量将其转换为PL / SQL,那么声明变量,将值弹入其中并将我的硬编码值更改为变量并不是一件简单的事情。并运行它。 注意:我知道我可以简单地将硬编码值更改为变量,例如:Department,:StartDate和:Type,但同样,我过于简化了示例。
我正面临着三个问题,我正试图解决这个问题:
1)使用带有声明变量的PL / SQL重写这个的最佳方法是什么? CTE现在必须进入INTO。但后来我一次只处理一行而不是整个表。因此,CTE'A'一次只能是一行,而CTE B只会看到单行而不是A等所有数据结果。我知道我很可能不得不使用CURSORS来遍历记录,似乎有点过于复杂。
2)输出现在必须使用DBMS_OUTPUT。对于多个记录,我将不得不使用带有FETCH(或FOR ... LOOP)的CURSOR。是?
3)在使用速度和资源方面,这与直接SQL有关的性能问题吗?
先谢谢你,如果我错过了一些非常明显的东西,我表示歉意!
答案 0 :(得分:1)
最简单的方法是将其包装成一个隐式的for循环
begin
for i in (select object_id, object_name
from user_objects
where rownum = 1) loop
-- Do something with the resultset
dbms_output.put_line (i.object_id || ' ' || i.object_name);
end loop;
end;
单行查询,无需预先定义变量。
答案 1 :(得分:1)
首先,这与CTE无关。这种行为与简单的select * from table
查询相同。区别在于使用T-SQL,查询进入一个隐式游标,返回给调用者。从Management Studio执行SP时,这很方便。结果集显示在数据窗口中,就像我们直接执行了查询一样。但这实际上是非标准行为。 Oracle具有更标准的行为,可以表示为“未定向到游标的任何查询的结果集必须指向变量”。当定向到变量时,查询必须只返回一行。
要复制T-SQL的行为,您只需显式声明并返回游标即可。然后调用代码从光标中获取整个结果集,但一次只能获取一行。你不方便Sql Developer或PL / SQL Developer将结果集转移到数据显示窗口,但你不能拥有一切。
但是,由于我们通常不会编写仅用于从IDE调用的SP,因此使用Oracle的显式游标比使用SQL Server的隐式游标更容易。只需将谷歌“oracle return ref cursor to caller”获得大量优质材料。