在 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 级对象 - 不存在。
您必须将变量值连接到from
和where
子句的动态语句中;您可以在选择列表中执行相同的操作(尽管它们需要包含在转义的单引号中,因为它们是字符串),或者在那里使用绑定变量:
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
用户身份连接时运行脚本。
永远记住,无论何时使用动态语句,都要首先使用 DBMS_OUTPUT 来验证正在生成的实际 SQL。这是调试动态查询的最佳方式。
-
您需要处理
NO_DATA_FOUND
异常,因为它会为所有与您使用的过滤器不匹配的表抛出错误。
【讨论】:
可能也需要处理 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的主要内容,如果未能解决你的问题,请参考以下文章