2016-03-14 140 views
3

我为拥有DW-ETL设置的公司工作。我需要编写一个查询,在WHEN - IN子句中查找超过2500+个值,并且在WHERE - IN子句中查找超过1000+个值。基本上,它看起来像下面这样:IN子句中的值限制Oracle

SELECT 
    ,user_id 
    ,CASE WHEN user_id IN ('user_n', +2500 user_[n+1]) THEN 1 
    ELSE 0 
    ,item_id 
FROM user_table 
    WHERE item_id IN ('item_n', +1000 item_[n+1]); 

正如你可能已经知道PL/SQL允许在IN条款最多1000个值的,所以我尝试添加OR - IN条款(如在其他计算器线程建议):

SELECT 
    ,user_id 
    ,CASE WHEN user_id IN ('user_n', +999 user_[n+1]) 
    OR user_id IN ('user_n', +999 user_[n+1]) 
    OR user_id IN ('user_n', +999 user_[n+1]) THEN 1 
    ELSE 0 END AS user_group 
    ,item_id 
FROM user_table 
    WHERE item_id IN ('item_n', +999 item_[n+1]) 
    OR item_id IN ('item_n', +999 item_[n+1]); 

注:我知道数学是在上面的例子是错误的,但你明白了吧

的问题是,查询有120分钟的最大执行时间和作业将自动为k illed。所以我搜索了一下我能找到的解决方案,看起来临时表可能是我正在寻找的解决方案,但是诚实地说,我发现没有任何例子足够清楚如何在表中包含我想要的值,以及在我原来的查询中使用这个表。甚至连ORACLE文档都没有多大帮助。

另一个潜在的问题是我的权利有限,而且我看到其他人提到在他们的公司他们没有创建临时表的权利。

一些我在我的研究中发现的信息的:

ORACLE documentation

StackOverflow thread

[StackOverflow thread 2]

另一种解决方案,我发现用元组代替,如THIS thread提到的(这是我的避风港没有尝试过),因为另一个用户提到的性能似乎受到了很大的影响。

任何有关如何使用临时表的指导或任何人有另一种处理此限制的方法都将不胜感激。

+3

哪里是你的'IN'子句的值? –

+0

您应该将所有这些值存储在临时表(user_n,item_n)中并使用IN到 – sagi

+0

@TomH这些值是由另一个部门“随机”选择的。我说随机,因为只知道为什么这些值是他们需要的,但我可以向你保证没有任何模式,例如BETWEEN不会帮助 – nachomasterCR

回答

0
create temporary table userids (userid int); 
insert into userids(...) 

然后加入或子查询

select ... 
where user_id in (select userid from userids); 
drop temporary table userids; 
4

创建所以没有撤销日志中创建一个全局临时表

CREATE GLOBAL TEMPORARY TABLE <table_name> (
<column_name> <column_data_type>, 
<column_name> <column_data_type>, 
<column_name> <column_data_type>) 
ON COMMIT DELETE ROWS; 

然后根据用户列表如何到达数据导入一个保温台,然后运行

select 'INSERT INTO global_temporary_table <column> values ' 
|| holding_table.column 
||';' 
FROM holding_table.column; 

This gives you insert statements as output which you run to insert the data. 

然后

SELECT <some_column> 
FROM <some_table> 
WHERE <some_value> IN 
(SELECT <some_column> from <global_temporary_table> 
+0

',但是如何将所有2500+特定的user_id插入表中?我不会再使用WHERE - IN子句吗?' – nachomasterCR

+0

从哪里获取user_ids列表。这个答案的中间部分有点混乱。您需要从值列表或基于该列表的循环中插入。 – Sigfried

+0

非常感谢,有读权限只是创建临时表的限制吗?我读过其他线程,它是.. – nachomasterCR

2

使用集合:

CREATE TYPE Ints_Table AS TABLE OF INT; 
CREATE TYPE IDs_Table AS TABLE OF CHAR(5); 

事情是这样的:

SELECT user_id, 
     CASE WHEN user_id MEMBER OF Ints_Table(1, 2, 3, /* ... */ 2500) 
      THEN 1 
      ELSE 0 
      END 
     ,item_id 
FROM user_table 
WHERE item_id MEMBER OF IDs_table('ABSC2', 'DITO9', 'KMKM9', /* ... */ 'QD3R5'); 

或者你可以使用PL/SQL来填充集合:

VARIABLE cur REFCURSOR; 

DECLARE 
    t_users Ints_Table; 
    t_items IDs_Table; 

    f  UTL_FILE.FILE_TYPE; 
    line VARCHAR2(4000); 
BEGIN 
    t_users.EXTEND(2500); 
    FOR i = 1 .. 2500 LOOP 
    t_users(t_users.COUNT) := i; 
    END LOOP; 

    // load data from a file 
    f := UTL_FILE.FOPEN('DIRECTORY_HANDLE','datafile.txt','R'); 
    IF UTL_FILE.IS_OPEN(f) THEN 
    LOOP 
    UTL_FILE.GET_LINE(f,line); 
    IF line IS NULL THEN EXIT; END IF; 
    t_items.EXTEND; 
    t_items(t_items.COUNT) := line; 
    END LOOP; 

    OPEN :cur FOR 
    SELECT user_id, 
      CASE WHEN user_id MEMBER OF t_users 
       THEN 1 
       ELSE 0 
       END 
      ,item_id 
    FROM user_table 
    WHERE item_id MEMBER OF t_items; 
END; 
/

PRINT cur; 

或者如果你正在使用另一种语言来调用查询,那么你可以传递集合作为绑定值(as shown here)。

+0

我刚刚了解收集,但他们可能没有用,在我的情况下,你可能建议它,因为我提出的数据造成混乱的方式。我使用的列表并不遵循(n + 1)模式,实际上没有模式,它是一个随机排列的5位字母数字值列表。所以列表可能是ABSC2,DITO9,KMKM9等。它仍然是一个集合用例吗? – nachomasterCR

+0

是的,请参阅我的更新。你可以从一个文件中加载这些值(参见中间部分),或者如果它们是以外部语言声明的话,可以将它作为绑定变量传入(参见java示例的末尾链接)。 – MT0

1

在PL/SQL中,您可以使用集合类型。你可以创建你自己是这样的:

create type string_table is table of varchar2(100); 

或使用现有的类型,如SYS.DBMS_DEBUG_VC2COLL这是VARCHAR2(1000)的表。

现在,你可以声明此类型的集合为每个列表,填充它,并在查询中使用它 - 是这样的:

declare 
    strings1 SYS.DBMS_DEBUG_VC2COLL := SYS.DBMS_DEBUG_VC2COLL(); 
    strings2 SYS.DBMS_DEBUG_VC2COLL := SYS.DBMS_DEBUG_VC2COLL(); 

    procedure add_string1 (p_string varchar2) is 
    begin 
     strings1.extend(); 
     strings1(strings.count) := p_string; 
    end; 

    procedure add_string2 (p_string varchar2) is 
    begin 
     strings2.extend(); 
     strings2(strings2.count) := p_string; 
    end; 
begin 
    add_string1('1'); 
    add_string1('2'); 
    add_string1('3'); 
    -- and so on... 
    add_string1('2500'); 

    add_string2('1'); 
    add_string2('2'); 
    add_string2('3'); 
    -- and so on... 
    add_string2('1400'); 

    for r in (
    select user_id 
     , case when user_id in table(strings2) then 1 else 0 end as indicator 
     , item_id 
     from user_table 
    where item_id in table(strings1) 
    ) 
    loop 
     dbms_output.put_Line(r.user_id||' '||r.indicator); 
    end loop; 
end; 
/
1

你可以用下面的例子来了解全局临时表和GTT的类型。

CREATE GLOBAL TEMPORARY TABLE GTT_PRESERVE_ROWS (ID NUMBER) ON COMMIT PRESERVE ROWS; 
INSERT INTO GTT_PRESERVE_ROWS VALUES (1); 
COMMIT; 
SELECT * FROM GTT_PRESERVE_ROWS; 
DELETE FROM GTT_PRESERVE_ROWS; 
COMMIT; 
TRUNCATE TABLE GTT_PRESERVE_ROWS; 
DROP TABLE GTT_PRESERVE_ROWS;--WONT WORK IF YOU DIDNOT TRUNCATE THE TABLE OR THE TABLE IS BEING USED IN SOME OTHER SESSION 

CREATE GLOBAL TEMPORARY TABLE GTT_DELETE_ROWS (ID NUMBER) ON COMMIT DELETE ROWS; 
INSERT INTO GTT_DELETE_ROWS VALUES (1); 
SELECT * FROM GTT_DELETE_ROWS; 
COMMIT; 
SELECT * FROM GTT_DELETE_ROWS; 
DROP TABLE GTT_DELETE_ROWS; 

但是正如你所说,你收到输入Excel文件,所以你可以简单地创建该表中的表和加载数据。数据加载后,您可以使用查询的IN子句中的数据。

select * from employee where empid in (select empid from temptable);