简体   繁体   中英

NOT IN is not working as expected with Listagg function

Below is the DDL of the table

  create or replace table  tempdw.blk_table;
   (
     db_name varchar,
     tbl_expr varchar
   );

   insert into  tempdw.blk_table  values ('edw','ABC%');
   insert into  tempdw.blk_table  values ('edw','EFG%');

   select * from tempdw.blk_table;

在此处输入图像描述

Below code is not working, expected output should not return any

select * from tempdw.blk_table where tbl_expr not in (
       select regexp_replace(regexp_replace(replace(listagg(tbl_expr,','),',','\',\''),'^','\''),'$','\'') from tempdw.blk_table);

When I run below code it works fine, Trying to understand why it's not working for above code

select * from tempdw.blk_table where tbl_expr NOT IN('ABC%','EFG%');

Au contraire The code is working just fine. You don't understand the difference between a string that has commas and a list of strings.

Unfortunately, it is rather hard to figure out what you do want to do, because your question does not explain that.

I can speculate that you want something like:

select bt.*
from blk_table bt
where db_name like tbl_expr;

This is just a guess, however.

with data as (
    select * from values ('edw','ABC%'),('edw','ABC%') v(db_name,tbl_expr )
)
select * from data 
where tbl_expr not in (
       select regexp_replace(regexp_replace(replace(listagg(tbl_expr,','),',','\',\''),'^','\''),'$','\'') from data);

does indeed give the results you don't want. aka:

DB_NAME TBL_EXPR
edw ABC%
edw ABC%

because your sub-query only has one row of results, because you have aggregated the two input into one row.

REGEXP_REPLACE( REGEXP_REPLACE( REPLACE( LISTAGG( TBL_EXPR,','),',','\',\''),'^','\''),'$','\'')
 'ABC%','ABC%'

and NOT IN is a exact match.. thus if we change from strings to numbers:

SELECT num, num in (2,3,4) FROM values (1),(3),(5) v(num);  

gives:

 NUM    NUM IN (2,3,4)
 1      0
 3      1
 5      0

so your NOT IN would only return strings that are not in the list of one you have... and given your list is the aggregate of the same input, that are by definition not that same.

back to strings..

SELECT str
    ,str in ('str_a', 'str_b')
    ,str not in ('str_a', 'str_b')
from values ('a'),('str_b') v(str);

gives:

STR    STR IN ('STR_A', 'STR_B')    STR NOT IN ('STR_A', 'STR_B')
a       0                           1
str_b   1                           0

Thus the results you are getting..

now I suspect you are want LIKE type behavior OR a REGEX match, but given you are building the list you know what you are doing there..

also note:

listagg(tbl_expr,',') AS a
    ,replace(a,',','\',\'') AS b
    ,regexp_replace(b,'^','\'') AS c
    ,regexp_replace(c,'$','\'') AS d

is the effect of what you are doing can be replaced with

listagg('\'' || tbl_expr || '\'',',')

unless you want strings with embedded comma to become independent "list" items..

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM