DB2 SQL 如何将一列拆分成可能的多行?
DB2 SQL How to split a column into potentially many rows?
我有一个包含 2 个字段(ID 和个人资料)的 table。
Table 有很多记录,但 ID 是唯一的。
Profile 长度为1000 个字符,每10 个字符基本上是一个Profile 代码。
有些 ID 可能只有 1 个代码,而其他 ID 有很多。
不知道table为什么要这样设计
所以记录可能类似于:
ID PROFILE
BOB BM BS DM FM IC IC6 IL IM JN101 MM XC XM XR
BILL ZZ XY
有没有办法拆分配置文件字段,生成新行
所以它看起来像:
ID PROFILE
BOB BM
BOB BS
BOB DM
...
BILL ZZ
BILL XY
我真的很想能够以某种方式创建视图。
试试这个:
用于 LUW 的 DB2
/*
WITH TAB (ID, PROFILE) AS
(
VALUES
('BOB', 'BM BS DM FM IC IC6 IL IM JN101 MM XC XM XR')
, ('BILL', 'ZZ XY')
)
*/
SELECT T.ID, V.TOK
FROM TAB T
, xmltable
(
'for $id in tokenize($s, " +") return <i>{string($id)}</i>'
passing T.PROFILE as "s"
columns
tok varchar(10) path '.'
) V;
用于 LUW 和 IBM i 的 DB2
IBM i 不支持(至少达到 7.4)FLWOR XQuery 表达式,因此您需要构建一个 XML 文档。
SELECT T.ID, V.TOK
FROM
TAB T
, XMLTABLE
(
'$doc/d/i'
PASSING XMLPARSE(DOCUMENT '<d><i>' || regexp_replace(trim(T.PROFILE), ' +', '</i><i>') || '</i></d>') as "doc"
COLUMNS
TOK VARCHAR(10) PATH '.'
) V;
尝试使用 split()。好像很慢。下面需要 14.817 秒。
WITH TAB (ID, PROFILE) AS
(
VALUES
('BOB', cast('BM BS DM FM IC IC6 IL IM JN101 MM XC XM XR' as char(1000))),
('BILL', cast('ZZ XY' as char(1000)))
)
SELECT a.id, b.element as profile
from tab a,
TABLE (systools.split(a.profile , ' ')) b
where b.element <> '';
与 Joachim 的回答仅用了 134 毫秒相比!
WITH TAB (ID, PROFILE) AS
(
VALUES
('BOB', cast('BM BS DM FM IC IC6 IL IM JN101 MM XC XM XR' as char(1000))),
('BILL', cast('ZZ XY' as char(1000)))
)
SELECT a.id, b.item as PROFILE
from tab a
,XMLTABLE('$doc/items/item'
PASSING XMLPARSE(DOCUMENT CAST( '<items><item>'
|| replace(a.profile , ' ' , '</item><item>')
|| '</item></items>' as CLOB
)
) as "doc"
COLUMNS
ITEM VARCHAR(255) PATH '.'
) b
WHERE b.item <> '';
有SYSTOOLS.SPLIT功能但是她的行为很奇怪...我不知道怎么回事...
使用长字符串作为参数非常慢且有错误!?!
CHECK> SELECT * FROM TABLE (SYSTOOLS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
[2020-10-22 17:04:59] 0 rows retrieved in 86 ms (execution: 51 ms, fetching: 35 ms)
[2020-10-22 17:04:59] [0F001][-423] [SQL0423] Pointeur SQLP_L2.VWORK incorrect.
[2020-10-22 17:04:59] [0F001][-423] [SQL0423] Pointeur SQLP_L2.VWORK incorrect.
[2020-10-22 17:30:29] 5 rows retrieved starting from 1 in 255 ms (execution: 41 ms, fetching: 214 ms)
CHECK> SELECT * FROM TABLE (SYSTOOLS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
[2020-10-22 17:30:29] 5 rows retrieved starting from 1 in 251 ms (execution: 39 ms, fetching: 212 ms)
CHECK> SELECT * FROM TABLE (SYSTOOLS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
[2020-10-22 17:30:30] 5 rows retrieved starting from 1 in 253 ms (execution: 39 ms, fetching: 214 ms)
CHECK> SELECT * FROM TABLE (SYSTOOLS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
IBM i 版 DB2
这是我从 this one
自定义的 SPLIT 函数
CREATE OR REPLACE FUNCTION UTILS.Split (String VARCHAR(32000), SEP VARCHAR(5))
RETURNS TABLE (ID INT, VALUE VARCHAR(256))
LANGUAGE SQL
DISALLOW PARALLEL
DETERMINISTIC
NOT FENCED
RETURN
WITH CTE (ID,StartString,StopString) AS
(
SELECT 1 AS ID, 1 AS StartString, LOCATE(SEP, String) AS StopString
FROM SYSIBM.SYSDUMMY1
WHERE LENGTH(SEP) > 0 AND LENGTH(String) > 0
UNION ALL
SELECT ID + 1, StopString + LENGTH(SEP), LOCATE(SEP, String, StopString + LENGTH(SEP))
FROM CTE
WHERE StopString > 0
)
SELECT
ID,
SUBSTRING(String,StartString,
CASE WHEN StopString = 0
THEN LENGTH(String)
ELSE StopString-StartString END
)
FROM CTE;
CHECK> SELECT * FROM TABLE (UTILS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
[2020-10-22 17:30:32] 5 rows retrieved starting from 1 in 50 ms (execution: 36 ms, fetching: 14 ms)
CHECK> SELECT * FROM TABLE (UTILS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
[2020-10-22 17:30:33] 5 rows retrieved starting from 1 in 63 ms (execution: 38 ms, fetching: 25 ms)
我有一个包含 2 个字段(ID 和个人资料)的 table。
Table 有很多记录,但 ID 是唯一的。
Profile 长度为1000 个字符,每10 个字符基本上是一个Profile 代码。
有些 ID 可能只有 1 个代码,而其他 ID 有很多。
不知道table为什么要这样设计
所以记录可能类似于:
ID PROFILE
BOB BM BS DM FM IC IC6 IL IM JN101 MM XC XM XR
BILL ZZ XY
有没有办法拆分配置文件字段,生成新行
所以它看起来像:
ID PROFILE
BOB BM
BOB BS
BOB DM
...
BILL ZZ
BILL XY
我真的很想能够以某种方式创建视图。
试试这个:
用于 LUW 的 DB2
/*
WITH TAB (ID, PROFILE) AS
(
VALUES
('BOB', 'BM BS DM FM IC IC6 IL IM JN101 MM XC XM XR')
, ('BILL', 'ZZ XY')
)
*/
SELECT T.ID, V.TOK
FROM TAB T
, xmltable
(
'for $id in tokenize($s, " +") return <i>{string($id)}</i>'
passing T.PROFILE as "s"
columns
tok varchar(10) path '.'
) V;
用于 LUW 和 IBM i 的 DB2
IBM i 不支持(至少达到 7.4)FLWOR XQuery 表达式,因此您需要构建一个 XML 文档。
SELECT T.ID, V.TOK
FROM
TAB T
, XMLTABLE
(
'$doc/d/i'
PASSING XMLPARSE(DOCUMENT '<d><i>' || regexp_replace(trim(T.PROFILE), ' +', '</i><i>') || '</i></d>') as "doc"
COLUMNS
TOK VARCHAR(10) PATH '.'
) V;
尝试使用 split()。好像很慢。下面需要 14.817 秒。
WITH TAB (ID, PROFILE) AS
(
VALUES
('BOB', cast('BM BS DM FM IC IC6 IL IM JN101 MM XC XM XR' as char(1000))),
('BILL', cast('ZZ XY' as char(1000)))
)
SELECT a.id, b.element as profile
from tab a,
TABLE (systools.split(a.profile , ' ')) b
where b.element <> '';
与 Joachim 的回答仅用了 134 毫秒相比!
WITH TAB (ID, PROFILE) AS
(
VALUES
('BOB', cast('BM BS DM FM IC IC6 IL IM JN101 MM XC XM XR' as char(1000))),
('BILL', cast('ZZ XY' as char(1000)))
)
SELECT a.id, b.item as PROFILE
from tab a
,XMLTABLE('$doc/items/item'
PASSING XMLPARSE(DOCUMENT CAST( '<items><item>'
|| replace(a.profile , ' ' , '</item><item>')
|| '</item></items>' as CLOB
)
) as "doc"
COLUMNS
ITEM VARCHAR(255) PATH '.'
) b
WHERE b.item <> '';
有SYSTOOLS.SPLIT功能但是她的行为很奇怪...我不知道怎么回事... 使用长字符串作为参数非常慢且有错误!?!
CHECK> SELECT * FROM TABLE (SYSTOOLS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
[2020-10-22 17:04:59] 0 rows retrieved in 86 ms (execution: 51 ms, fetching: 35 ms)
[2020-10-22 17:04:59] [0F001][-423] [SQL0423] Pointeur SQLP_L2.VWORK incorrect.
[2020-10-22 17:04:59] [0F001][-423] [SQL0423] Pointeur SQLP_L2.VWORK incorrect.
[2020-10-22 17:30:29] 5 rows retrieved starting from 1 in 255 ms (execution: 41 ms, fetching: 214 ms)
CHECK> SELECT * FROM TABLE (SYSTOOLS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
[2020-10-22 17:30:29] 5 rows retrieved starting from 1 in 251 ms (execution: 39 ms, fetching: 212 ms)
CHECK> SELECT * FROM TABLE (SYSTOOLS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
[2020-10-22 17:30:30] 5 rows retrieved starting from 1 in 253 ms (execution: 39 ms, fetching: 214 ms)
CHECK> SELECT * FROM TABLE (SYSTOOLS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
IBM i 版 DB2
这是我从 this one
自定义的 SPLIT 函数CREATE OR REPLACE FUNCTION UTILS.Split (String VARCHAR(32000), SEP VARCHAR(5))
RETURNS TABLE (ID INT, VALUE VARCHAR(256))
LANGUAGE SQL
DISALLOW PARALLEL
DETERMINISTIC
NOT FENCED
RETURN
WITH CTE (ID,StartString,StopString) AS
(
SELECT 1 AS ID, 1 AS StartString, LOCATE(SEP, String) AS StopString
FROM SYSIBM.SYSDUMMY1
WHERE LENGTH(SEP) > 0 AND LENGTH(String) > 0
UNION ALL
SELECT ID + 1, StopString + LENGTH(SEP), LOCATE(SEP, String, StopString + LENGTH(SEP))
FROM CTE
WHERE StopString > 0
)
SELECT
ID,
SUBSTRING(String,StartString,
CASE WHEN StopString = 0
THEN LENGTH(String)
ELSE StopString-StartString END
)
FROM CTE;
CHECK> SELECT * FROM TABLE (UTILS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
[2020-10-22 17:30:32] 5 rows retrieved starting from 1 in 50 ms (execution: 36 ms, fetching: 14 ms)
CHECK> SELECT * FROM TABLE (UTILS.SPLIT('4182511-S_4182701-X_4182702-X_4182703-X_4182796-S' , '_'))
[2020-10-22 17:30:33] 5 rows retrieved starting from 1 in 63 ms (execution: 38 ms, fetching: 25 ms)