在 Oracle 中生成动态 SQL

Posted

技术标签:

【中文标题】在 Oracle 中生成动态 SQL【英文标题】:Generating Dynamic SQL in Oracle 【发布时间】:2016-02-19 10:50:33 【问题描述】:

我的 plsql 代码有问题并尝试了几乎所有方法。现在我正在失去解决我的问题的想法和力量:)

情况是我想在所有表模式中搜索分配给变量 v_ss 的特定字符串并将其打印到 DBMS_OUTPUT。我知道这种情况有现成的解决方案,但我想自己编写代码。下面的代码在我的 v_stmt 中给了我一个“表不存在”的错误。我假设这个选择不能识别 rec.column_name,但是为什么?

这是我的代码:

DECLARE
 v_stmt VARCHAR2(1000);
 v_ss VARCHAR2(30) := 'Argentina';

 v_own ALL_TAB_COLUMNS.OWNER%TYPE;
 v_tab_nam ALL_TAB_COLUMNS.TABLE_NAME%TYPE;
 v_col_nam ALL_TAB_COLUMNS.COLUMN_NAME%TYPE;

 CURSOR cur_asc IS
   SELECT t.owner, t.table_name, t.column_name
   FROM SYS.ALL_TAB_COLUMNS t
   WHERE t.OWNER LIKE 'HR'
     AND t.DATA_TYPE LIKE 'VARCHAR2';


BEGIN
  FOR rec IN cur_asc LOOP
    v_stmt := 'SELECT rec.owner, rec.table_name, rec.column_name FROM rec.table_name WHERE rec.column_name LIKE :1';
    EXECUTE IMMEDIATE v_stmt INTO v_own, v_tab_nam, v_col_nam USING v_ss;
    DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam);
  END LOOP;


END;
/

Error report -
ORA-00942: table or view does not exist
ORA-06512: at line 19
00942. 00000 -  "table or view does not exist"
*Cause:    
*Action:

您能否解释一下我的错误以及如何修复它?提前致谢

【问题讨论】:

【参考方案1】:

动态语句在看不到您的 PL/SQL 变量的上下文中执行,因此当它运行时rec.table_name 等被解释为 SQL 级对象 - 不存在。

您必须将变量值连接到fromwhere 子句的动态语句中;您可以在选择列表中执行相同的操作(尽管它们需要包含在转义的单引号中,因为它们是字符串),或者在那里使用绑定变量:

v_stmt := 'SELECT :owner, :table_name, :column_name FROM '
  || rec.table_name || ' WHERE ' || rec.column_name || ' LIKE :ss';
EXECUTE IMMEDIATE v_stmt INTO v_own, v_tab_nam, v_col_nam
USING rec.owner, rec.table_name, rec.column_name, v_ss;

您不能将绑定变量用于对象标识符,因此需要对这些部分进行串联。

除非您以 HR 用户身份运行,在这种情况下您可以使用 user_tables 而不是 all_tables,您还需要在查询中指定架构(正如 Tony 和 Lalit 提到的);硬编码 HR 或使用查询的所有者:

v_stmt := 'SELECT :owner, :table_name, :column_name FROM '
  || rec.owner || '.' || rec.table_name
  || ' WHERE ' || rec.column_name || ' LIKE :ss';

这对于所有不包含恰好一个匹配值的表都会出错 - 如果动态选择获得零行或多行。但这是一个单独的问题。

【讨论】:

【参考方案2】:

v_stmt := 'SELECT rec.owner, rec.table_name, rec.column_name FROM rec.table_name WHERE rec.column_name LIKE :1';

    您的动态 sql 语句格式错误。如果您将变量括在单引号之间,那么它们将被视为文字而不是变量。 您必须在 table_name 之前添加 schema 前缀,否则您必须在以 HR 用户身份连接时运行脚本。
v_stmt := 'SELECT '''||rec.owner||''', '''|| rec.table_name||''', ' ||rec.column_name||' FROM '||rec.owner||'。' ||rec.table_name||' WHERE '||rec.column_name||'喜欢:1';

永远记住,无论何时使用动态语句,都要首先使用 DBMS_OUTPUT 来验证正在生成的实际 SQL。这是调试动态查询的最佳方式。

    您需要处理 NO_DATA_FOUND 异常,因为它会为所有与您使用的过滤器不匹配的表抛出错误。
SQL> 声明 2 v_stmt VARCHAR2(1000); 3 v_ss VARCHAR2(30) := '阿根廷'; 4 v_own ALL_TAB_COLUMNS.OWNER%TYPE; 5 v_tab_nam ALL_TAB_COLUMNS.TABLE_NAME%TYPE; 6 v_col_nam ALL_TAB_COLUMNS.COLUMN_NAME%TYPE; 7 光标 cur_asc 8 是 9 选择 t.owner, 10 t.table_name, 11 t.column_name 12 来自 SYS.ALL_TAB_COLUMNS t 13 所有者喜欢“HR”的地方 14 AND t.DATA_TYPE LIKE 'VARCHAR2'; 15 开始 16 FOR REC IN cur_asc 17 循环 18 v_stmt := 'SELECT '''||rec.owner||''', '''|| rec.table_name||''', ' 19 || rec.column_name||' FROM '||rec.owner||'。' 20 || rec.table_name||' WHERE '||rec.column_name||'喜欢:1'; 21 开始 22 立即执行 v_stmt 进入 v_own, 23 v_tab_nam, 24 v_col_nam 使用 v_ss; 25 DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam); 26 例外 27 当 NO_DATA_FOUND THEN 28 空; 29 结束; 30 结束循环; 31 结束; 32 / 人力资源:国家:阿根廷 PL/SQL 过程成功完成。

【讨论】:

可能也需要处理 too_many_rows 吗?如果 v_ss 有一个通配符,可能有不同的但使用 'like' 可能意味着仍然有多个匹配项。 也许不适用于没有任何操作/重复的标准 HR 模式。但总的来说是的,需要处理。似乎 OP 这样做是出于某种学习目的,否则应该完全避免这种方法。【参考方案3】:

这里有两个问题:

    您需要将表名和列名连接到动态 SQL 中,因为“SELECT rec.owner...”正在尝试从别名为 rec 的表中选择名为 owner 的列,它没有引用您的for 循环记录。 由于表中可能没有匹配的行或匹配的行很多,所以不能使用select into,需要使用游标。

试试这个:

DECLARE
 v_stmt VARCHAR2(1000);
 v_ss VARCHAR2(30) := 'Argentina';

 v_own ALL_TAB_COLUMNS.OWNER%TYPE;
 v_tab_nam ALL_TAB_COLUMNS.TABLE_NAME%TYPE;
 v_col_nam ALL_TAB_COLUMNS.COLUMN_NAME%TYPE;

 CURSOR cur_asc IS
   SELECT t.owner, t.table_name, t.column_name
   FROM ALL_TAB_COLUMNS t
   WHERE t.DATA_TYPE LIKE 'VARCHAR2'
   AND ROWNUM < 10;

 c SYS_REFCURSOR;

BEGIN
  FOR rec IN cur_asc LOOP
    v_stmt := 'SELECT ''' || rec.owner || ''',''' || rec.table_name || ''', ''' || rec.column_name || ''' FROM ' || rec.table_name || ' WHERE ' || rec.column_name || ' LIKE :1';
    OPEN c FOR v_stmt USING v_ss;
    LOOP
      FETCH c INTO v_own, v_tab_nam, v_col_nam;
      EXIT WHEN c%NOTFOUND;
      DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam);
    END LOOP;
    CLOSE c;
  END LOOP;
END;
/

【讨论】:

您打错字了,因为USER_ 视图没有所有者列。

以上是关于在 Oracle 中生成动态 SQL的主要内容,如果未能解决你的问题,请参考以下文章

ORA-00904 Linq 在 Oracle 的 SQL 查询中生成错误

在 Oracle PL/SQL 中生成字母数字序列

在 SELECT 查询 SQL 中生成动态日期列

如何在JSP网页中生成动态图表

带有在子查询中生成的值的 Oracle SQL 更新语句

如何在 Oracle 中生成 xml 空值?