2012-02-08 63 views
2

可能重複:
How to best split csv strings in oracle 9iOracle:使用IN子句和文本字段?

我有一些地方有一個VARCHAR2(100)SUBID有逗號分隔的數據舊數據:

empno subid 
1  1, 3, 2 
2  18,19, 3, 6, 9 

我需要寫的相當於

select * 
    from table 
where id in (select SUBID from subidtable where empno = 1) 

有沒有辦法在Oracle中實現這一點?

編輯:

增加了一些說明。我需要對IN子句對單個行中存儲在字符串中的值執行操作,而不是所有行。

+0

你是說,你需要找到'table'其中的記錄'table.id'在* any *'subidtable.subid'中?就像您發佈的示例數據一樣,您是否需要在'(1,2,3,6,9,18,19)'中用'id'在'table'中查找所有記錄? – ruakh 2012-02-08 15:05:10

+2

你可以規範這張桌子嗎?即你可以創建另一個表並將逗號分隔的值轉換爲單個數字嗎? – 2012-02-08 15:07:06

+0

在你的例子中,'table'中'id'字段的數據類型是什麼?只要數據類型匹配(或將其轉換),您應該可以在Oracle中使用IN條款。 – ProfessionalAmateur 2012-02-08 15:07:49

回答

3

你可以,但它有點難看。取決於Oracle版本

您可以使用this askTom thread的變體將數據解析到集合中並在您的SQL語句中使用集合。這應該可以在8.1.5以後的任何版本的Oracle中使用,但多年來語法已經變得更簡單了。

SQL> create or replace type myTableType as table 
    2  of varchar2 (255); 
    3/

Type created. 

SQL> ed 
Wrote file afiedt.buf 

    1 create or replace 
    2  function in_list(p_string in varchar2) return myTableType 
    3 as 
    4  l_string  long default p_string || ','; 
    5  l_data   myTableType := myTableType(); 
    6  n    number; 
    7 begin 
    8  loop 
    9   exit when l_string is null; 
10   n := instr(l_string, ','); 
11   l_data.extend; 
12   l_data(l_data.count) := 
13    ltrim(rtrim(substr(l_string, 1, n-1))); 
14   l_string := substr(l_string, n+1); 
15  end loop; 
16  return l_data; 
17* end; 
SQL>/

Function created. 

SQL> select ename 
    2 from emp 
    3 where empno in (select column_value 
    4      from table(in_list('7934, 7698, 7521'))); 

ENAME 
---------- 
WARD 
BLAKE 
MILLER 

您還可以使用正則表達式爲this StackOverflow thread

SQL> ed 
Wrote file afiedt.buf 

    1 select ename 
    2 from emp 
    3 where empno in (select regexp_substr(str, '[^,]+',1,level) 
    4      from (select '7934, 7698, 7521' str from dual) 
    5*     connect by level <= regexp_count(str,'[^,]+')) 
SQL>/

ENAME 
---------- 
WARD 
MILLER 
BLAKE 
+0

我希望有更好的方法來做到這一點。數據庫中實際上包含一個包含具有確切代碼的函數的包,甚至可以在評論中引用特定的asktom文章。所以我猜想,直到我們可以修復桌子設計,套餐纔會生活。 – chris 2012-02-08 19:38:15

2

討論如果你可以「修復」你的表結構有一個1:一對多的關係,使得您的subidtable每行只包含oneid,這是你最好的選擇。

如果您不能,那麼您可以獲得人們在網絡上編碼的功能之一。這些接受一個字符串並將數據作爲一組返回。這裏的問題是,它們被設計爲只接受一個字符串並返回一個值表,不接受一個字符串表格...

由於此數據似乎有點被黑客入侵,只需要一次性的破解解決方案,只需最少的代碼。在這種情況下,您可以嘗試...

SELECT 
    * 
FROM 
    table 
WHERE 
    EXISTS (SELECT * FROM subidtable WHERE (',' || subid || ',') LIKE ('%,' || table.id || ',%')) 

但是要警告,它的比例非常糟糕。因此,如果您在任一表中都有大量數據,那麼預計性能會降低。


編輯

至於你的編輯現在顯示你永遠只處理從subidtable表中的一個字符串,分割功能選項變得更容易了很多實現。請參閱Justin的回答:)

對上面的「簡單黑客」的修改將是...

SELECT 
    * 
FROM 
    table 
WHERE 
    (SELECT ',' || subid || ',' FROM subidtable WHERE empno=1) LIKE ('%,' || table.id || ',%') 
0

這是沒有辦法優雅的「立即執行」,但在特定情況下濫用這可能工作:

DECLARE 
    i INTEGER; 
    subid VARCHAR2(100) := '18,19, 3, 6, 9'; 
BEGIN 

    EXECUTE IMMEDIATE 'select 1 from dual where 6 in (' || subid || ')' 
    INTO i; 

    dbms_output.put_line('returned: ' || i); 
EXCEPTION 
    WHEN others THEN 
    dbms_output.put_line('Exception: ' || SQLERRM); 
END;