1. 程式人生 > >db2 將逗號分隔數據轉換為多值IN列表

db2 將逗號分隔數據轉換為多值IN列表

分隔符 zone 單獨 index anr -a 分類 而是 失敗

將逗號分隔數據轉換為多值IN列表

2010-03-15 11:16:59| 分類: 數據庫技術|舉報|字號 訂閱

技術分享 技術分享 下載LOFTER我的照片書 |

原文:http://book.csdn.net/bookfiles/530/10053017906.shtml

6.11 將分隔數據轉換為多值IN列表

問題

已經有了分隔數據,想要將其轉換為WHERE子句IN列表中的項目。考慮下面的字符串:

7654,7698,7782,7788

要將該字符串用在WHERE子句中,但是下面的SQL語句是錯誤的,因為EMPNO是一個數值列:

select ename,sal,deptno

from emp

where empno in ( ‘7654,7698,7782,7788‘ )

因為EMPNO是一個數值列,而此IN列表是一個字符串值,所以此SQL語句會失敗。現要將此字符串轉換為用逗號分解的數值列表。

解決方案

表面上看SQL應該將分隔字符串作為一個分隔值列表對待,但是實際情況不是這樣。當SQL遇到括在引號中的逗號時,並不知道此符號表示多值列表,SQL必須將括在引號中的內容作為一個整體對待,也就是一個字符串值。因此必須將字符串分解為各個單獨的EMPNO。這種解決方案的關鍵就是需要遍歷字符串,但並不是一個字符一個字符地遍歷,而是要將這個字符串轉換為有效的EMPNO值。

DB2

通過遍歷傳遞給IN列表的字符串,可以很輕松地將其轉換為若幹行。在這裏函數ROW_NOMBER、LOCATE和SUBSTR尤其有用:

1 select empno,ename,sal,deptno

2 from emp

3 where empno in (

4 select cast(substr(c,2,locate(‘,‘,c,2)-2) as integer) empno

5 from (

6 select substr(csv.emps,cast(iter.pos as integer)) as c

7 from (select ‘,‘||‘7654,7698,7782,7788‘||‘,‘ emps

8 from t1) csv,

9 (select id as pos

10 from t100 ) iter

11 where iter.pos <= length(csv.emps)

12 ) x

13 where length(c) > 1

14 and substr(c,1,1) = ‘,‘

15 ) y

MySQL

通過遍歷傳遞給IN列別的字符串,可以很輕松地將其轉換為若幹行:

1 select empno, ename, sal, deptno

2 from emp

3 where empno in

4 (

5 select substring_index(

6 substring_index(list.vals,‘,‘,iter.pos),‘,‘,-1) empno

6 from (select id pos from t10) as iter,

7 (select ‘7654,7698,7782,7788‘ as vals

8 from t1) list

9 where iter.pos <=

10 (length(list.vals)-length(replace(list.vals,‘,‘,‘‘)))+1

11 ) x

Oracle

通過遍歷傳遞給IN列別的字符串,可以很輕松地將其轉換為若幹行。這裏函數ROWNUM,SUBSTR和INSTR尤其有用:

1 select empno,ename,sal,deptno

2 from emp

3 where empno in (

4 select to_number(

5 rtrim(

6 substr(emps,

7 instr(emps,‘,‘,1,iter.pos)+1,

8 instr(emps,‘,‘,1,iter.pos+1) -

9 instr(emps,‘,‘,1,iter.pos)),‘,‘)) emps

10 from (select ‘,‘||‘7654,7698,7782,7788‘||‘,‘ emps from t1) csv,

11 (select rownum pos from emp) iter

12 where iter.pos <= ((length(csv.emps)-

13 length(replace(csv.emps,‘,‘)))/length(‘,‘))-1

14 )

Postgres

通過遍歷傳遞給IN列別的字符串,可以很輕松地將其轉換為若幹行。使用函數 SPLIT_PART可以簡化將字符串解析為單獨的數值列表的工作:

1 select ename,sal,deptno

2 from emp

3 where empno in (

4 select cast(empno as integer) as empno

5 from (

6 select split_part(list.vals,‘,‘,iter.pos) as empno

7 from (select id as pos from t10) iter,

8 (select ‘,‘||‘7654,7698,7782,7788‘||‘,‘ as vals

9 from t1) list

10 where iter.pos <=

11 length(list.vals)-length(replace(list.vals,‘,‘,‘‘))

12 ) z

13 where length(empno) > 0

14 ) x

SQL Server

通過遍歷傳遞給IN列別的字符串,可以很輕松地將其轉換為若幹行。這裏函數ROW_NUMBER、CHARINDEX和SUBSTRING尤其有用:

1 select empno,ename,sal,deptno

2 from emp

3 where empno in (select substring(c,2,charindex(‘,‘,c,2)-2) as empno

4 from (

5 select substring(csv.emps,iter.pos,len(csv.emps)) as c

6 from (select ‘,‘+‘7654,7698,7782,7788‘+‘,‘ as emps

7 from t1) csv,

8 (select id as pos

9 from t100) iter

10 where iter.pos <= len(csv.emps)

11 ) x

12 where len(c) > 1

13 and substring(c,1,1) = ‘,‘

14 ) y

討論

這種解決方案中第一步,也是最重要的一步就是遍歷字符串。一旦完成了這步操作,剩下的操作就是使用DBMS提供的函數來將字符串解析為單獨的數值。

DB2和SQL Server

內聯視圖X(第6~11行)遍歷字符串,這裏用的是“穿越”字符串的思想,所以其每一行都比其上一行少一個字符:

,7654,7698,7782,7788,

7654,7698,7782,7788,

654,7698,7782,7788,

54,7698,7782,7788,

4,7698,7782,7788,

,7698,7782,7788,

7698,7782,7788,

698,7782,7788,

98,7782,7788,

8,7782,7788,

,7782,7788,

7782,7788,

782,7788,

82,7788,

2,7788,

,7788,

7788,

788,

88,

8,

,

註意,因為整個字符串是由逗號(分界符)括起來的,所以不需要特殊的檢查來確定字符串從哪裏開始及到哪裏結束:

下一步是只保留要用於內部列表中的值。除了最後只有單獨逗號的一行之外,保留以逗號開頭的行。使用SUBSTR或SUBSTRING函數來識別哪些行以逗號開頭,並保留在此行中到下一個逗號之間的所有字符。此操作完成後,將字符串轉換為數值,這樣就可以用來正確地跟數值列EMPNO運算了(行4~14):

EMPNO

------

7654

7698

7782

7788

最後,使用在子查詢中的結果來返回想要得到的行。

MySQL

內聯視圖(行5~9)遍歷字符串。第10行的表達式通過查找逗號(分隔符)的數量來確定在字符串中有多少個值並加1。函數SUBSTRING_INDEX(第6行)返回在字符串中第n個逗號(分隔符)之前(到它左邊)的所有字符:

+---------------------+

| empno |

+---------------------+

| 7654 |

| 7654,7698 |

| 7654,7698,7782 |

| 7654,7698,7782,7788 |

+---------------------+

這些行隨後傳遞給SUBSTRING_INDEX(第5行)函數的另一次調用,此時第n個分隔符參數為-1,這表示在第n個分隔符右邊所有的值需要保留。

+-------+

| empno |

+-------+

| 7654 |

| 7698 |

| 7782 |

| 7788 |

+-------+

最後一步是將結果加入到一個子查詢中。

Oracle

第一步是遍歷字符串:

select emps,pos

from (select ‘,‘||‘7654,7698,7782,7788‘||‘,‘ emps

from t1) csv,

(select rownum pos from emp) iter

where iter.pos <=

((length(csv.emps)-length(replace(csv.emps,‘,‘)))/length(‘,‘))-1

EMPS POS

--------------------- ----------

,7654,7698,7782,7788, 1

,7654,7698,7782,7788, 2

,7654,7698,7782,7788, 3

,7654,7698,7782,7788, 4

有多少行返回,就表示在列表中有多少個值。POS值對於查詢是至關重要的,因為要用它來將字符串分解為單獨的值。使用SUBSTR和INSTR函數來分解字符串,POS用來確定在每個字符串中第n個分隔符的位置。由於字符串是用逗號括起來的,所以不需要特殊的檢測手段來確定字符串的開始位置與結束位置。傳遞給SUBSTR,INSTR(7~9行)確定第n個和第n+1個分隔符的位置。用下一個逗號的位置值(字符串中下一個逗號的位置)減去當前逗號的位置值(字符串中當前逗號的位置),就可以從字符串中提取出每個值。

select substr(emps,

instr(emps,‘,‘,1,iter.pos)+1,

instr(emps,‘,‘,1,iter.pos+1) -

instr(emps,‘,‘,1,iter.pos)) emps

from (select ‘,‘||‘7654,7698,7782,7788‘||‘,‘ emps

from t1) csv,

(select rownum pos from emp) iter

where iter.pos <=

((length(csv.emps)-length(replace(csv.emps,‘,‘)))/length(‘,‘))-1

EMPS

-----------

7654,

7698,

7782,

7788,

最後一步就是刪除每個值後面的逗號,將其轉換為數值並且將其插入到子查詢中。

PostgreSQL

內聯視圖Z(6~9行)遍歷字符串,返回行的數量也就是在字符串中有多少值。要找出在字符串中有多少值,用帶有分隔符的字符串長度減去不帶分隔符的字符串長度即可(第9行),函數SPLIT_PART用來分解字符串,該函數查找第n個分隔符之前的值。

SELECT列表.vals,

split_part(list.vals,‘,‘,iter.pos) as empno,

iter.pos

from (select id as pos from t10) iter,

(select ‘,‘||‘7654,7698,7782,7788‘||‘,‘ as vals

from t1) list

where iter.pos <=

length(list.vals)-length(replace(list.vals,‘,‘,‘‘))

vals | empno | pos

-----------------------+-------+-----

,7654,7698,7782,7788, | | 1

,7654,7698,7782,7788, | 7654 | 2

,7654,7698,7782,7788, | 7698 | 3

,7654,7698,7782,7788, | 7782 | 4

,7654,7698,7782,7788, | 7788 | 5

最後一步就是將返回值(EMPNO)轉換為數值,並且插入到子查詢中。

db2 將逗號分隔數據轉換為多值IN列表