立即执行不与 select into 一起使用

Posted

技术标签:

【中文标题】立即执行不与 select into 一起使用【英文标题】:Execute immediate not working with select into 【发布时间】:2021-04-28 13:35:24 【问题描述】:

我有下面的代码,可以正常工作。

SET SERVEROUTPUT ON;

DECLARE
    cols CLOB;
BEGIN
    SELECT
        LISTAGG('"'
                || column_name
                || '"', ',') WITHIN GROUP(
            ORDER BY
                column_name
        )
    INTO cols
    FROM
        all_tab_columns
    WHERE
        lower(table_name) = 'd_dialler_brut'
        AND column_name LIKE 'REASON%';

    dbms_output.put_line(cols);
END;

但是,如果我尝试以下代码,则会引发以下错误

错误报告 - ORA-00905: 缺少关键字 ORA-06512: 在第 20 行 00905. 00000 - “缺少关键字”

SET SERVEROUTPUT ON;

DECLARE
    cols        CLOB;
    vstr_cols   CLOB;
BEGIN
    vstr_cols := q'[
    SELECT
        LISTAGG('"'
                || column_name
                || '"', ',') WITHIN GROUP(
            ORDER BY
                column_name
        )
    INTO cols
    FROM
        all_tab_columns
    WHERE
        lower(table_name) = 'd_dialler_brut'
        AND column_name LIKE 'REASON%']'
    ;
    EXECUTE IMMEDIATE ( vstr_cols );
    dbms_output.put_line(cols);
END;

第二个代码有什么问题?如何避免错误?

【问题讨论】:

【参考方案1】:

从您正在构建的SELECT 语句中删除INTO 子句并将其添加到EXECUTE IMMEDIATE 语句中

vstr_cols := q'[
SELECT
    LISTAGG('"'
            || column_name
            || '"', ',') WITHIN GROUP(
        ORDER BY
            column_name
    )
FROM
    all_tab_columns
WHERE
    lower(table_name) = 'd_dialler_brut'
    AND column_name LIKE 'REASON%']'
;

EXECUTE IMMEDIATE vstr_cols
             INTO cols;

当然,我首先会质疑在这里使用动态 SQL 是否合适。如果您可以使用静态 SQL 做某事,那么您真的应该使用静态 SQL。

我还强烈建议您在执行 SQL 语句之前记录或使用dbms_output 将其写出。否则,你会让自己更难调试。

【讨论】:

谢谢。不确定在执行之前记录 SQL 语句是什么意思。另外,在这种情况下,静态 SQL 与动态 SQL 有什么好处? @jeiv - 记录意味着将其写入表或您的应用程序实现的任何记录机制,以便当您遇到错误时,您可以准确地看到动态构建的查询。将事物保留为静态 SQL 有很多好处——你在编译时而不是在运行时得到编译错误,当出现语法错误时,你会得到更具体的错误,Oracle 会为你跟踪依赖关系,你不必担心关于 SQL 注入攻击之类的事情,您的代码更易于阅读、编写、调试和维护。 @jeiv - 是的,如果你想创建一个表,你需要使用动态 SQL。但是,您希望在存储过程中创建表的情况非常少见。 Oracle 中的表在安装应用程序时创建一次,而不是在运行时创建。我亲自编写 PL/SQL 代码已经有 20 年了,我还没有遇到过我真的想在存储过程中创建表的情况。我见过一些第三方系统在运行时定期创建表,而且它们通常很难管理。 @jeiv - 这真的取决于你在做什么。如果您正在执行数据仓库加载并通过完全重新加载写入暂存表,那么执行截断和重新加载并非不合理。通过merge 语句进行增量加载通常更快,但这可能并不总是可行的。我不确定您要通过动态透视到新创建的表来解决什么问题,所以我不确定该建议什么。但鉴于其他代码不能使用动态创建的表中的数据,除非它也是动态的,我会小心的。 @jeiv - 比删除并重新创建表格更好?是的。识别增量并通过merge 加载它可能是最有效的。

以上是关于立即执行不与 select into 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

甲骨文。无法理解 FOR 如何与子查询 SELECT INTO 一起使用

如何将 SQL INSERT INTO SELECT 与 codeigniter 一起使用

高手,执行insert into select 语句慢,一般是啥原因造成的了

执行 SELECT INTO 时获取 SQL 错误代码

MYSQL insert into select 锁表问题

NamedParameterJdbcTemplate 无法执行 insert into select