在 Oracle 中生成动态 SQL
Generating Dynamic SQL in Oracle
我的 plsql 代码有问题,尝试了几乎所有的方法。现在我失去了解决我的问题的想法和力量:)
案例是我想在所有表架构中搜索分配给变量 v_ss 的特定字符串并将其打印到 DBMS_OUTPUT。我知道这种情况有现成的解决方案,但我想自己编写代码。下面的代码给我一个错误,在我的 v_stmt 中 "table does not exist"。我假设这个 select 不识别 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:
你能给我解释一下我的错误以及如何修复它吗?提前致谢
动态语句在看不到你的PL/SQL变量的上下文中执行,所以当它运行s rec.table_name
等被解释为SQL级对象 - 不存在。
您必须将变量值连接到 from
和 where
子句的动态语句中;你可以在 select 列表中做同样的事情(尽管它们需要用转义的单引号括起来,因为它们是字符串),或者在那里使用绑定变量:
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
,您还需要在查询中指定架构(如托尼和拉利特提到);硬编码 HR 或使用查询的所有者:
v_stmt := 'SELECT :owner, :table_name, :column_name FROM '
|| rec.owner || '.' || rec.table_name
|| ' WHERE ' || rec.column_name || ' LIKE :ss';
对于所有不完全包含一个匹配值的表,这将出错 - 如果动态 select 获得零行或多于一行。但这是一个单独的问题。
这里有 2 个问题:
- 您需要将 table 和列名连接到动态 SQL 中,因为 'SELECT rec.owner...' 正在尝试 select 来自一个名为
owner
的列table 别名 rec
,它不引用您的 for 循环记录。
- 由于 table 中可能没有匹配行或有很多匹配行,您不能使用
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;
/
v_stmt := 'SELECT rec.owner, rec.table_name, rec.column_name FROM rec.table_name WHERE rec.column_name LIKE :1';
- 您的动态 sql 语句格式不正确。如果您将变量括在单引号之间,那么它们将被视为文字而不是变量。
- 您必须在 table_name 之前为 架构 添加前缀,否则当您作为
HR
用户连接时,您必须 运行 脚本。
v_stmt := 'SELECT '''||rec.owner||''', '''|| rec.table_name||''', '
||rec.column_name||' FROM '||rec.owner||'.'
||rec.table_name||' WHERE '||rec.column_name||' LIKE :1';
永远记住,每当使用动态语句时,总是使用 DBMS_OUTPUT 来首先验证实际生成的 SQL。这是调试动态查询的最佳方式。
- 您需要处理
NO_DATA_FOUND
异常,因为它会为所有与您正在使用的过滤器不匹配的表抛出错误。
SQL> DECLARE
2 v_stmt VARCHAR2(1000);
3 v_ss VARCHAR2(30) := 'Argentina';
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 CURSOR cur_asc
8 IS
9 SELECT t.owner,
10 t.table_name,
11 t.column_name
12 FROM SYS.ALL_TAB_COLUMNS t
13 WHERE t.OWNER LIKE 'HR'
14 AND t.DATA_TYPE LIKE 'VARCHAR2';
15 BEGIN
16 FOR rec IN cur_asc
17 LOOP
18 v_stmt := 'SELECT '''||rec.owner||''', '''|| rec.table_name||''', '
19 || rec.column_name||' FROM '||rec.owner||'.'
20 || rec.table_name||' WHERE '||rec.column_name||' LIKE :1';
21 BEGIN
22 EXECUTE IMMEDIATE v_stmt INTO v_own,
23 v_tab_nam,
24 v_col_nam USING v_ss;
25 DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam);
26 EXCEPTION
27 WHEN NO_DATA_FOUND THEN
28 NULL;
29 END;
30 END LOOP;
31 END;
32 /
HR:COUNTRIES:Argentina
PL/SQL procedure successfully completed.
我的 plsql 代码有问题,尝试了几乎所有的方法。现在我失去了解决我的问题的想法和力量:)
案例是我想在所有表架构中搜索分配给变量 v_ss 的特定字符串并将其打印到 DBMS_OUTPUT。我知道这种情况有现成的解决方案,但我想自己编写代码。下面的代码给我一个错误,在我的 v_stmt 中 "table does not exist"。我假设这个 select 不识别 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:
你能给我解释一下我的错误以及如何修复它吗?提前致谢
动态语句在看不到你的PL/SQL变量的上下文中执行,所以当它运行s rec.table_name
等被解释为SQL级对象 - 不存在。
您必须将变量值连接到 from
和 where
子句的动态语句中;你可以在 select 列表中做同样的事情(尽管它们需要用转义的单引号括起来,因为它们是字符串),或者在那里使用绑定变量:
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
,您还需要在查询中指定架构(如托尼和拉利特提到);硬编码 HR 或使用查询的所有者:
v_stmt := 'SELECT :owner, :table_name, :column_name FROM '
|| rec.owner || '.' || rec.table_name
|| ' WHERE ' || rec.column_name || ' LIKE :ss';
对于所有不完全包含一个匹配值的表,这将出错 - 如果动态 select 获得零行或多于一行。但这是一个单独的问题。
这里有 2 个问题:
- 您需要将 table 和列名连接到动态 SQL 中,因为 'SELECT rec.owner...' 正在尝试 select 来自一个名为
owner
的列table 别名rec
,它不引用您的 for 循环记录。 - 由于 table 中可能没有匹配行或有很多匹配行,您不能使用
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;
/
v_stmt := 'SELECT rec.owner, rec.table_name, rec.column_name FROM rec.table_name WHERE rec.column_name LIKE :1';
- 您的动态 sql 语句格式不正确。如果您将变量括在单引号之间,那么它们将被视为文字而不是变量。
- 您必须在 table_name 之前为 架构 添加前缀,否则当您作为
HR
用户连接时,您必须 运行 脚本。
v_stmt := 'SELECT '''||rec.owner||''', '''|| rec.table_name||''', ' ||rec.column_name||' FROM '||rec.owner||'.' ||rec.table_name||' WHERE '||rec.column_name||' LIKE :1';
永远记住,每当使用动态语句时,总是使用 DBMS_OUTPUT 来首先验证实际生成的 SQL。这是调试动态查询的最佳方式。
- 您需要处理
NO_DATA_FOUND
异常,因为它会为所有与您正在使用的过滤器不匹配的表抛出错误。
SQL> DECLARE 2 v_stmt VARCHAR2(1000); 3 v_ss VARCHAR2(30) := 'Argentina'; 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 CURSOR cur_asc 8 IS 9 SELECT t.owner, 10 t.table_name, 11 t.column_name 12 FROM SYS.ALL_TAB_COLUMNS t 13 WHERE t.OWNER LIKE 'HR' 14 AND t.DATA_TYPE LIKE 'VARCHAR2'; 15 BEGIN 16 FOR rec IN cur_asc 17 LOOP 18 v_stmt := 'SELECT '''||rec.owner||''', '''|| rec.table_name||''', ' 19 || rec.column_name||' FROM '||rec.owner||'.' 20 || rec.table_name||' WHERE '||rec.column_name||' LIKE :1'; 21 BEGIN 22 EXECUTE IMMEDIATE v_stmt INTO v_own, 23 v_tab_nam, 24 v_col_nam USING v_ss; 25 DBMS_OUTPUT.put_line(v_own || ':' || v_tab_nam || ':' || v_col_nam); 26 EXCEPTION 27 WHEN NO_DATA_FOUND THEN 28 NULL; 29 END; 30 END LOOP; 31 END; 32 / HR:COUNTRIES:Argentina PL/SQL procedure successfully completed.