可以使用动态部分扩展静态SQL语句吗?

时间:2011-05-31 14:25:39

标签: sql oracle plsql oracle11g dynamic-sql

我想创建一个Oracle包,其中有一个执行某些动态SQL的过程。如果我使用EXECUTE IMMEDIATE进行动态操作,这没有问题,但如果查询的静态部分可以编码为静态(以便进行编译时检查),那就更好了。

完全动态查询的示例:

-- v_stmt is built dynamically.
v_stmt := 'SELECT count(*) FROM <here some joins> WHERE <here some conditions>';
EXECUTE IMMEDIATE v_stmt
  USING v_param1, v_param2
  RETURNING INTO v_count;

我试图使FROM-part静态的例子:

-- v_stmt is built dynamically.
v_stmt := 'SELECT count(*) FROM my_package.my_function(:param1, :param2) WHERE <here some conditions>';
EXECUTE IMMEDIATE v_stmt
  USING v_param1, v_param2
  RETURNING INTO v_count;

FUNCTION my_function(
  i_param1 IN VARCHAR2,
  i_param2 IN NUMBER
)
RETURN SYS_REFCURSOR
AS
  v_cursor SYS_REFCURSOR;
BEGIN
  -- Open a cursor for different queries depending on params.
  IF i_param2 = 1 THEN
      OPEN v_cursor FOR <some static query>;
  ELSE
      OPEN v_cursor FOR <some other static query>;
  END IF;
  RETURN v_cursor;
END;

这不起作用,因为无法从SYS_REFCURSOR中选择(至少这是我在Google上找到的)。

有没有办法达到这个目标?

编辑:根据要求,以下是一些示例:

静态查询:

SELECT a.*, ca.CUS_ID FROM adresses a INNER JOIN customer_adresses ca ON (ca.adr_id = a.adr_id);
SELECT p.*, cp.CUS_ID FROM persons p INNER JOIN customer_persons cp ON (cp.per_id = p.per_id);

然后像以下示例一样动态扩展它们:

-- Checks if there is an adress in the customer where the zip is null.
SELECT count(*) FROM <static adresses query> q WHERE q.cus_id = :param1 AND a.zip IS NULL;
-- Checks if there is at least one person in the customer.
SELECT count(*) FROM <static persons query> q WHERE q.cus_id = :param1;

3 个答案:

答案 0 :(得分:2)

抱歉,为什么需要这样做?通过引入一个根据参数列表返回不同类型的数据/表的函数,您似乎已经结束了复杂化。非常令人困惑的imo。此外,你必须在某个地方做这项工作,你只是想在这个函数中隐藏它(如果param1 =这个那么x,如果param1 =然后是y ......)

此外,即使你确实实现了一个游标函数(甚至是流水线),在这种情况下也是一个坏主意,因为你要强迫Oracle完成它不一定需要做的工作(忽略所有现在进行上下文切换)。为了得到一个计数,你可以让Oracle获取每一行的结果,然后计算。很多时候,Oracle可以进行快速全索引扫描来获取计数(当然,取决于查询)。并且,如果在缓冲区缓存中找到块,则每次运行多次相同的查询将不需要执行所有工作。我要求你使用直接SQL和使用返回游标的函数多次运行计数。你可能会感到惊讶。据我所知(请看这个)新的11g函数结果缓存不适用于流水线函数或返回引用游标的函数(以及由于依赖表而导致的其他问题)。

所以,我所说的是为什么不这样做:从...中选择count(1)到v_variable;

如果你想隐藏和模块化,那就知道你可能会失去什么。

答案 1 :(得分:1)

您可能希望在function1中打开一个查询,然后将其结果作为一个表格传递给function2,然后将该where子句添加到此“table”

在这种情况下,您需要将function1重写为pipelined table function

v_stmt := 'SELECT count(*) FROM table(my_package.my_function(:param1, :param2)) WHERE <here some conditions>';
EXECUTE IMMEDIATE v_stmt
  USING v_param1, v_param2
  RETURNING INTO v_count;

CREATE TYPE object_row_type AS OBJECT (
  OWNER              VARCHAR2(30),
  OBJECT_TYPE        VARCHAR2(18),
  OBJECT_NAME        VARCHAR2(30),
  STATUS             VARCHAR2(7)
);


CREATE TYPE object_table_type AS TABLE OF object_row_type;


FUNCTION my_function(
  i_param1 IN VARCHAR2,
  i_param2 IN NUMBER
)
RETURN object_table_type PIPELINED AS
BEGIN 

答案 2 :(得分:0)

您可以使用Oracle expression filter对表达式进行编译时检查。

它可能比其他解决方案更复杂,但如果你真的需要验证你的条件,它会有所帮助。