2016-03-14 73 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);