如何在使用变量的动态查询中指定 IN 子句?

Posted

技术标签:

【中文标题】如何在使用变量的动态查询中指定 IN 子句?【英文标题】:How do you specify IN clause in a dynamic query using a variable? 【发布时间】:2012-01-23 05:45:48 【问题描述】:

在 PL/SQL 中,您可以使用连接指定 IN 运算符的值:

v_sql := 'select field1
from table1
where field2 in (' || v_list || ')';

是否可以使用变量来做同样的事情?

v_sql := 'select field1
from table1
where field2 in (:v_list)'; 

如果有,怎么做?

编辑:参考 Marcin 的回答,我如何从结果表中进行选择?

declare

cursor c_get_csv_as_tables is
select in_list(food_list) food_list
from emp_food
where emp_type = 'PERM';

cursor c_get_food_list (v_food_table varchar2Table)is
select *
from v_food_table;

begin
    for i in c_get_csv_as_tables loop
        for j in c_get_food_list(i.food_list) loop
            dbms_output.put_line(j.element);
        end loop;
    end loop;
end;

我收到以下错误:

ORA-06550: line 10, column 6:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 9, column 1:
PL/SQL: SQL Statement ignored
ORA-06550: line 15, column 34:
PLS-00364: loop index variable 'J' use is invalid
ORA-06550: line 15, column 13:
PL/SQL: Statement ignored

【问题讨论】:

PL/SQL use VARRAY in IN CLAUSE的可能重复 @Sathya 我不明白...怎么一样?我需要传递一个绑定变量。有没有类似的方法可以做到这一点? 我没有意识到你想绑定。话虽如此,afaik,你不能使用这样的绑定变量。 但是你仍然可以像@Sathya 给你的链接中那样绑定一个可变数组 How do I check for a IN condition against a dynamic list in Oracle?的可能重复 【参考方案1】:

就像在@Sathya 链接中一样,你可以绑定可变数组(我以@Codo 为例):

CREATE OR REPLACE TYPE str_tab_type IS VARRAY(10) OF VARCHAR2(200);
/
DECLARE
  l_str_tab str_tab_type;
  l_count NUMBER;
  v_sql varchar2(3000);
BEGIN
  l_str_tab := str_tab_type();
  l_str_tab.extend(2);
  l_str_tab(1) := 'TABLE';
  l_str_tab(2) := 'INDEX';

  v_sql := 'SELECT COUNT(*) FROM all_objects WHERE object_type IN (SELECT COLUMN_VALUE FROM TABLE(:v_list))';

  execute immediate v_sql into l_count using l_str_tab;

  dbms_output.put_line(l_count);
END;
/

更新:第一个命令可以替换为:

CREATE OR REPLACE TYPE str_tab_type IS TABLE OF VARCHAR2(200);
    /

然后调用:

l_str_tab.extend(1);

当你添加一个值时

【讨论】:

啊我现在明白了。但是这个解决方案似乎需要我们知道设置数组长度的最大值数。它还需要将 CSV 字符串转换为数组。第二个可以,但第一个应该很难。 我使用 VARRAY 只是因为它在原始示例中。您可以使用不同的集合,例如 VARCHAR2(200) 的 TABLE。可以为每个添加的值调用扩展方法(转换 CSV 时)【参考方案2】:

很遗憾,您不能像这样绑定列表,但是您可以使用表函数。阅读this

以下是基于您的代码的使用示例:

declare

cursor c_get_csv_as_tables is
select in_list(food_list) food_list
from emp_food
where emp_type = 'PERM';

cursor c_get_food_list (v_food_table varchar2Table)is
select column_value food
from TABLE(v_food_table);

begin
    for i in c_get_csv_as_tables loop
        for j in c_get_food_list(i.food_list) loop
            dbms_output.put_line(j.food);
        end loop;
    end loop;
end;

我在这里使用了column_value 伪列

【讨论】:

谢谢!我会等一天看看有没有其他解决方案,然后标记为解决方案。 我遇到了这种方法的问题。由于我没有“列”名称,如何从结果表中进行选择?【参考方案3】:

绑定变量可以在带有“in”子句的Oracle SQL 查询中使用。

在 10g 中工作;其他版本我不知道。

绑定变量是最多 4000 个字符的 varchar。

示例:绑定包含逗号分隔值列表的变量,例如

:bindvar = 1,2,3,4,5

select * from mytable
  where myfield in
    (
      SELECT regexp_substr(:bindvar,'[^,]+', 1, level) items
      FROM dual
      CONNECT BY regexp_substr(:bindvar, '[^,]+', 1, level) is not null
    );

【讨论】:

【参考方案4】:

根据@Marcin 的回答,你不能这样做,但是,还有一点要补充,因为你的查询应该实际工作,即运行。

简单地说,您不能对表或列使用绑定变量。不仅如此,绑定变量它们被假定为一个字符,所以如果你想要一个数字,你必须使用to_number(:b1)等。

这是您的查询失败的地方。当您传入一个字符串时,Oracle 假定您的整个列表是一个字符串。因此,您正在有效地运行:

select field1
  from table1
where field2 = v_list

您没有理由不能以不同的方式执行此操作。我将假设您正在动态创建v_list,这意味着您需要做的就是以不同的方式创建此列表。据称,一系列or 条件是:-),与使用in 没有什么不同。

据称,我的意思是永远不要依赖未经测试的东西。尽管 Tom 在链接中确实说可能存在性能限制,但不能保证它不会比开始使用 in 更快。最好的办法是对您的查询和他的查询运行跟踪,看看有什么区别(如果有的话)。

SQL> set serveroutput on
SQL>
SQL> declare
  2
  3    l_string varchar2(32767);
  4    l_count number;
  5
  6  begin
  7
  8      for xx in ( select rownum as rnum, a.*
  9                    from user_tables a
 10                   where rownum < 20 ) loop
 11
 12        if xx.rnum = 1 then
 13          l_string := 'table_name = ''' || xx.table_name || '''';
 14        else
 15          l_string := l_string || ' or table_name = ''' || xx.table_name || '
''';
 16        end if;
 17
 18      end loop;
 19
 20      execute immediate 'select count(*)
 21                           from user_tables
 22                          where ' || l_string
 23                           into l_count
 24                                ;
 25
 26      dbms_output.put_line('count is ' || l_count);
 27
 28  end;
 29  /
count is 19

PL/SQL procedure successfully completed.

【讨论】:

以上是关于如何在使用变量的动态查询中指定 IN 子句?的主要内容,如果未能解决你的问题,请参考以下文章

ORDER BY 子句“IN()”中指定的值,但在泛型 SQL 和 Informix 中

如何重写此 MySQL 查询,使其不会引发此错误:您无法在 FROM 子句中指定目标表 'crawlLog' 进行更新?

如何从 Android SQLite 查询的动态 ArrayList 编写 WHERE IN 子句

是否可以使用 group by 子句在查询结果中指定代表组的行的选择?

Pro *C 查询 IN 子句中的多个动态值

如何解决 MySQL 错误“您不能在 FROM 子句中指定目标表 X 进行更新”? [复制]