oracle - 如何在 PL/SQL 中重用查询 block 以最大程度地减少代码样板和对象意大利面

标签 oracle plsql

各位 PL/SQL 专家,这也许是一个无法回答的问题,但也许有人有一个出色的解决方案。

今天,我发现自己复制粘贴了一条非常长的 SQL 语句,以便在一种情况下可以有条件地加入另一个表,而在另一种情况下则不能这样做。显然,这让我这个程序员很恼火,他现在有两个 SQL 副本需要维护:

IF <condition>
THEN
  FOR rec_data IN (SELECT <complex SQL, pages long>)
  LOOP
    PIPE ROW(....);
  END LOOP;
ELSE
  FOR rec_data IN (SELECT *
                     FROM (SELECT <same complex SQL, pages long> ) x,
                          <another table> y)
  LOOP
    PIPE ROW(....);
  END LOOP;
END IF;

如何避免拥有相同 SQL 的两个副本?

选项:

  1. 使用动态 SQL 并有条件地将主 SQL 与执行附加连接的另一个查询 block 包装在一起。缺点:动态 SQL 更难阅读和使用,因为所有转义单引号、维护绑定(bind)变量列表等。而且,它是字符串操作,感觉编码很差。而且,我必须定义一个记录类型,其中包含要从中获取的所有列。更多的工作,更多的冗余编码(我需要为数十个函数执行此操作,而不仅仅是一个,所以这很重要)。

  2. 创建另一个运行核心 SQL 并返回行的管道函数,然后在我的顶级函数中以两种不同的方式进行查询。缺点:我的代码现在被拆分为一个完全不同的对象,现在我不仅必须为行创建类型,而且它们必须是 SQL 类型。很多定义只是为了一个函数的本地使用。

  3. 创建一个全局临时表并使用核心 SQL 加载它。直接从中选择或有条件地选择并加入。缺点:现在我有一个在代码对象外部使用硬编码列定义定义的表,仅供该代码对象使用。请记住,我有几十个这样的函数要编写,而且我不想要对象意大利面条。

  4. 为核心 SQL 创建 View 。有条件查询。缺点:要维护单独的对象,而且无法将变量深入到 View 中。

  5. 无条件地仅使用较长的版本(带有条件连接的版本),但在其中使用奇特的 CASE/DECODE 来有效禁用连接(例如 DECODE(<condition>,x.join_key,NULL) = y.join_key )。缺点:这相当 hacky,并且如果您有条件加入的附加“表”是昂贵的 PL/SQL 管道函数,则可能不太容易获得性能优势。我试图避免在不需要输出时调用该函数。

迫切需要的是避免复制粘贴,避免字符串操作,并避免仅仅为了获取本地所需的内容而定义列定义。就像我们需要做这样的事情(从我的 PL/SQL 游标变量中读取 SQL...这是伪代码,我知道你不能按照编写的那样执行此操作!)

  DECLARE
    CURSOR cur_data IS
    SELECT <complex SQL>;
  BEGIN
    IF <condition>
    THEN
      FOR rec_data IN (SELECT *
                         FROM cur_data)
      LOOP
        PIPE ROW(....);
      END LOOP;
    ELSE
      FOR rec_data IN (SELECT *
                         FROM cur_data,
                              other_table)
      LOOP
        PIPE ROW(....);
      END LOOP;
    END IF;

有什么疯狂的、很棒的想法吗?

最佳答案

动态 SQL 是处理样板代码的一项很好的技术。在大多数编程语言中,动态代码都是有问题的,因为很难推断编程语言和环境,而且字符串操作很丑陋。 Oracle 有一些功能可以缓解这些问题。

Oracle提供了数据字典和PL/Scope等工具来让我们更容易地推理我们的数据库环境和代码。通过使用 ALL_TAB_COLUMNS 等 View 的简单 SQL 语句,可以轻松理解 SQL 对象。 .

Oracle 具有可以显着清理字符串操作代码的功能。我们可以通过组合多行字符串、替代引用机制和简单的模板系统来构建更清晰的代码,而不是无休止的串联和使用大量的引号。

多行字符串意味着简单地使用 native 行尾而不是连接 CHR(10)||CHR(13) 。 (我很困惑为什么 2023 年的某些语言不支持如此简单的概念。)替代引用语法允许我们指定自己的分隔符,例如 q'!...!' , q'[...]' ,和q'<...>' - 不再有双引号。模板化不需要花哨的引擎,只需要简单的变量语法和 REPLACE功能。

declare
    -- Create a SQL template with well-formatted code.
    -- The variables will be replaced later.
    -- In trivial examples, templating may need more lines of code than concatenation,
    -- but for REAL code, defining the template in one place up front is a life-saver.
    v_sql clob :=
    q'[
        insert into some_table
        select 'a' b, 'c' d, '#VALUE1#'
        from #TABLE1#
        #WHERE1#
    ]'
begin
    -- Set variables.
    -- (In real code, you may need to worry about SQL injection and the performance of
    --  using literals instead of bind variables.)
    v_value1 := 'A';
    v_table1 := 'dual';
    v_where := 'where 1=1'
    ...

    -- Replace the variables here.
    v_sql := replace(replace(replace(v_sql
        , '#VALUE1#', v_value1)
        , '#TABLE1#', v_table1)
        , '#WHERE1#', v_where);

    -- Printing the SQL is useful for debugging.
    dbms_output.put_line(v_sql);

    -- Run the SQL.
    -- (This will get more complicated for bind variables and retrieving results.)
    execute immediate v_sql;
end;
/

关于oracle - 如何在 PL/SQL 中重用查询 block 以最大程度地减少代码样板和对象意大利面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75292627/

相关文章:

sql - 如何在Oracle中四舍五入到下一个10?

xml - 甲骨文PLSQL : how to parse XML and insert into table

sql - ORA-00205 : error in identifying control file, 检查警报日志以获取更多信息

oracle - Devart dotConnect 适用于 Oracle 和企业库自定义提供商映射

oracle - 检查数据库中是否存在表 - PL SQL

java - 如何通过 Exception getCause() 循环查找带有详细消息的根本原因

xml - 使用 PL/SQL 解析大型 XML 文件

java - 通过可调用语句从 Java 调用存储过程时出错

database - 绑定(bind)感知光标匹配说明

oracle - 如何从返回多个 OUT 值的过程中仅访问一个 OUT 值?