【问题标题】:Oracle SQL : Retrieving non-existing values from IN clauseOracle SQL:从 IN 子句中检索不存在的值
【发布时间】:2014-10-15 17:48:21
【问题描述】:

有以下查询:

select table_name
from user_tables
where table_name in ('A','B','C','D','E','F');

假设只有 user_tables 记录 B、C 和 F 存在,我想检索不存在的值 A、D 和 E。这是一个简单的例子,在现实世界中列表可能很大。

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    生成假行的好方法是使用标准集合,例如sys.odcivarchar2list

    select
        tables_to_check.table_name,
        case when user_tables.table_name is null then 'No' else 'Yes'end table_exists
    from
    (
        select column_value table_name
        from table(sys.odcivarchar2list('does not exist', 'TEST1'))
    ) tables_to_check
    left join user_tables
        on tables_to_check.table_name = user_tables.table_name
    order by tables_to_check.table_name;
    
    
    TABLE_NAME       TABLE_EXISTS
    ----------       ------------
    TEST1            Yes
    does not exist   No
    

    【讨论】:

    • @SylvainLeroux 只是一个懒惰的捷径。 :) 但你是对的,通常最好明确引用列。我更新了答案。
    【解决方案2】:

    如果您在 Table1 中有要检查的所有这些表的列表,那么您可以使用 NOT EXISTS 子句

    select name
    from Table1 T1
    where not exists ( select 1 from  
                       user_tables U
                       where T1.name = U.table_name)
    

    【讨论】:

    • 不,这些是任意值,在其他表中不存在。
    • 我会说这是正确的解决方案。这里的“Table1”是一个实际的表还是基于编码列表的某个表函数,或者只是需要提供多少个表名的问题。 +1。
    【解决方案3】:

    唯一的方法是将IN clause字符串转换为值表来使用NOT EXISTS。(CTE)

    虽然这不是一个干净的解决方案。因为IN子句表达式的最大长度将是4000,包括逗号..

    WITH MY_STRING(str) AS
    (
      SELECT q'#'A','B','C','D','E','F'#' FROM DUAL
    ),
    VALUES_TABLE AS
    (
      SELECT TRIM(BOTH '''' FROM REGEXP_SUBSTR(str,'[^,]+',1,level)) as table_name FROM MY_STRING
      CONNECT BY LEVEL <= REGEXP_COUNT(str,',')
    )
    SELECT ME.* FROM VALUES_TABLE ME
    WHERE NOT EXISTS
    (SELECT 'X' FROM user_tables u
     WHERE u.table_name = ME.table_name);
    

    【讨论】:

      【解决方案4】:

      你不能。至少必须将这些值输入到临时表中才能执行所需的操作。此外,Oracle 的 IN 子句列表不能很大(即不超过 1000 个值)。

      【讨论】:

      • 好吧,你说得对,300,700, 800 可能很长——分析起来并不大
      • 有没有办法使用 XMLtype 或一些伪结构?
      【解决方案5】:

      您是否仅限于以逗号分隔列表的形式接收这些值?

      1. 不是用源值创建一个逗号分隔的列表,而是填充一个数组(或一个表)。
      2. 将数组传递到 pl/sql 过程(或从表中拉出游标)。
      3. 遍历数组(游标)并使用动态游标从 user_tables where table_name = value_pulled 中选择 count(table_name)。
      4. count(table_name) = 0 时插入表 B。
      5. 然后你可以从表B中选择所有

        select * from tab1;
        ------------------
        A
        B
        C
        D 
        E
        F 
        
        Create or replace procedure proc1 as  
        
        cursor c is select col1 from tab1;
        r tab1.col1%type;
        i number;
        
        begin  
        
        open c;
        loop
          fetch c into r;
          exit when c%notfound; 
          select count(tname) into i from tab where tname = r;
            if i = 0 then 
              v_sql := 'insert into tab2 values ('''||r||''');
              execute immediate v_sql; 
              commit;
            end if; 
        end loop;
        close c;
        end proc1;
        
        select * from tab2;
        ------------------
        A
        D 
        E 
        

      如果这不是一次性的,那么手头有这个过程会很方便。

      【讨论】:

        猜你喜欢
        • 2016-04-25
        • 2016-06-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多