可以用动态部分扩展静态 SQL 语句吗?
Posted
技术标签:
【中文标题】可以用动态部分扩展静态 SQL 语句吗?【英文标题】:Possible to extend static SQL statements with dynamic parts? 【发布时间】:2011-05-31 14:25:39 【问题描述】:我想创建一个 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 部分设为静态的示例:
-- 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;
【问题讨论】:
这可能会也可能不会,这取决于您的具体要求。请给我们提供词干(静态)查询和一些动态扩展的具体示例。 @APC 我已经添加了一些示例。 【参考方案1】:抱歉,为什么需要这样做?似乎您通过引入一个函数来使事情复杂化,该函数将根据参数列表返回不同类型的数据/表。非常令人困惑的海事组织。此外,你必须在某个地方做这项工作,你只是想把它隐藏在这个函数中(在 if param1=this then x if param1=that then y...)
此外,即使您确实实现了游标函数(甚至是流水线的),在这种情况下也不是一个好主意,因为您将迫使 Oracle 去做它不一定需要做的工作(忽略所有现在的上下文切换)。要获得计数,您可以让 Oracle 获取每行结果然后计数。很多时候,Oracle 可以通过快速的全索引扫描来获取计数(当然取决于查询)。如果在缓冲区缓存中找到块,通常多次运行相同的查询不需要每次都完成所有工作。我会挑战您使用直接 SQL 与使用返回游标的函数多次运行计数。你可能会感到惊讶。据我所知(检查我),新的 11g 函数结果缓存不适用于流水线函数或返回 ref 游标的函数(以及其他问题,例如由于依赖表而导致的失效)。
所以,我要说的是为什么不直接做:select count(1) into v_variable from ...;
如果您想隐藏和模块化,那么只需知道您可能会丢失什么。
【讨论】:
你说得对,仅仅在编译时检查我的语句就很复杂了。我现在已将所有内容更改为完全动态的,并将使用单元测试来检查查询。【参考方案2】:您可能希望在 function1 中打开一个查询,然后将其结果作为一个表传递给 function2,然后它将一个 where 子句添加到这个“表”
在这种情况下,您需要将您的 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
【讨论】:
我的函数(包含静态查询)返回不同类型的表(例如:客户、项目、人员等)。我认为这不适用于对象表,因为对象具有固定的结构,对吧? 是的,但您不必从表函数中返回固定类型。 “Oracle 具有三种特殊的 SQL 数据类型,使您能够动态封装和访问任何其他 SQL 类型(包括对象和集合类型)的类型描述、数据实例和数据实例集。您还可以使用这三种特殊类型来创建匿名 (即,未命名的)类型,包括匿名集合类型。这些类型是 SYS.ANYTYPE、SYS.ANYDATA 和 SYS.ANYDATASET。SYS.ANYDATA 类型在某些情况下可用作表函数的返回值。”跨度> 来自Oracle docs 那么我的函数会返回 ANYDATASET 而不是 SYS_REFCURSOR?是否可以使用原始表中的名称从 ANYDATASET 中选择列?【参考方案3】:您可以使用Oracle expression filter 对表达式进行编译时检查。
它可能比其他解决方案更复杂,但如果您确实需要验证您的条件,它可能会有所帮助。
【讨论】:
以上是关于可以用动态部分扩展静态 SQL 语句吗?的主要内容,如果未能解决你的问题,请参考以下文章
动态SQL是什么??什么是静态SQL,动态SQL的动态体现在哪里???