Select DB2/DB2400 中逗号分隔字符串的一部分

Select a portion of a comma delimited string in DB2/DB2400

我需要 select 一个逗号分隔字符串中的值,仅使用 SQL。这可能吗?

Data
A      B       C    
1     Luigi     Apple,Banana,Pineapple,,Citrus

我需要 select 具体 2nd item in column C,在本例中是香蕉。我需要帮助。我无法创建新的 SQL 函数,我只能使用 SQL。这是 as400 所以 SQL 有点老技术了。

更新.. 在@Sandeep 的帮助下,我们能够想出

SELECT xmlcast(xmlquery('$x/Names/Name[2]' passing xmlparse(document CONCAT(CONCAT('<?xml version="1.0" encoding="UTF-8" ?><Names><Name>',REPLACE(ODWDATA,',','</Name><Name>')),'</Name></Names>')) as "x") as varchar(1000)) FROM ACL00

我遇到了这个错误

Keyword PASSING not expected. Valid tokens: ) ,. 

新更新。使用Oracle的INSTR

的UDF解决的问题

如果你只想要第二项,你可以使用子字符串函数:

DECLARE @TABLE TABLE
(
    A INT,
    B VARCHAR(100),
    C VARCHAR(100)
)

DECLARE @NTH INT = 3

INSERT INTO @TABLE VALUES (1,'Luigi','Apple,Banana,Pineapple,,Citrus')

SELECT REPLACE(REPLACE(CAST(CAST('<Name>'+ REPLACE(C,',','</Name><Name>') +'</Name>' AS XML).query('/Name[sql:variable("@NTH")]') AS VARCHAR(1000)),'<Name>',''),'</Name>','') FROM @TABLE

我假设我不使用 db2,因此以下语法可能不适用,但该方法有效。

在 Oracle 中,我会使用 INSTR() 和 SUBSTR(),Google 建议为 db2 使用 LOCATE() 和 SUBSTR()

使用 LOCATE 获取第一个逗号的位置,并在 SUBSTR 中使用该值获取第一个逗号后开始的 YourColumn 的末尾

SUBSTR(YourColumn, LOCATE(YourColumn, ',') + 1)

您从 "Apple,Banana,Pineapple,,Citrus" 开始,现在应该 "Banana,Pineapple,,Citrus",所以我们再次对上面的 returned 字符串使用 LOCATE 和 SUBSTR。

SUBSTR(SUBSTR(YourColumn, LOCATE(YourColumn, ',') + 1), 1, LOCATE(SUBSTR(YourColumn, LOCATE(YourColumn, ',') + 1), ',') - 1)

第一个 SUBSTR 正在获取字符串的右侧,因此我们只需要一个起始位置参数,第二个 SUBSTR 正在获取字符串的左侧,因此我们需要两个,即起始位置和长度 return.

我现在正在回答我自己的问题。使用 built in functions within AS400

是不可能做到这一点的

您必须创建 Oracle INSTR

的 UDF

在 STRSQL 中输入它会创建一个名为 INSTRB 的新函数

CREATE FUNCTION INSTRB (C1 VarChar(4000), C2 VarChar(4000), N integer, M integer)
 RETURNS Integer
 SPECIFIC INSTRBOracleBase
 LANGUAGE SQL
 CONTAINS SQL
 NO EXTERNAL ACTION
 DETERMINISTIC
BEGIN ATOMIC
DECLARE Pos, R, C2L Integer;

SET C2L = LENGTH(C2);

IF N > 0 THEN
   SET (Pos, R) = (N, 0);
   WHILE R < M AND Pos > 0 DO
      SET Pos = LOCATE(C2,C1,Pos);
         IF Pos > 0 THEN
         SET (Pos, R) = (Pos + 1, R + 1);
      END IF;
   END WHILE;

   RETURN (Pos - 1)*(1-SIGN(M-R));
ELSE
   SET (Pos, R) = (LENGTH(C1)+N, 0);
   WHILE R < M AND Pos > 0 DO
      IF SUBSTR(C1,Pos,C2L) = C2 THEN
         SET R = R + 1;
      END IF;
      SET Pos = Pos - 1;
   END WHILE;

   RETURN (Pos + 1)*(1-SIGN(M-R));
END IF;

END

然后到 select 逗号分隔字符串中的第 n 个分隔值...在本例中是第 14 个

利用新函数使用此查询

SELECT SUBSTRING(C,INSTRB(C,',',1,13)+1,INSTRB(C,',',1,14)-INSTRB(C,',',1,13)-1) FROM TABLE

一个更漂亮的解决方案IMO将封装一个递归通用Table表达式(递归CTE aka RCTE) C 列中的数据生成结果 TABLE [即用户定义 Table 函数(Table UDF aka UDTF)] 然后使用标量子 select 选择有效的 record\row 数字。

  select
    a
  , b
  , ( select S.token_vc
      from table( split_tokens(c) ) as S
      where S.token_nbr = 2
    ) as "2nd Item of column C"
  from The_Table /* in OP described with columns a,b,c but no DDL */

然而更漂亮的方法是使同一个 RCTE 的结果成为标量值,以便允许简单地作为标量 UDF 调用,有效行号 [作为另一个参数] 明确定义哪个元素 select.

  select
    a
  , b
  , split_tokens(c, 2) as "2nd Item of column C"
  from The_Table /* in OP described with columns a,b,c but no DDL */

后者可能更有效,将 RCTE 生成的行数据限制为仅所需的 numbered 标记和 numbered[=28 之前的标记=] 令牌。我无法评论 效率 对 CPU 和存储的影响,与提供的任何其他 answers 相比,但是我自己在临时存储实现和 RCTE 结果的整体速度方面的经验是积极的,尤其是当其他行 selection 限制了必须为整个查询生成的派生-table 结果的数量时请求。

UDF [and\or UDTF 和实现它们的 RCTE] 留作 reader 的练习;主要是因为我没有发布支持递归 table 表达式的系统。如果问 [例如在对此 answer] 的评论中,我可以提供 untested 代码源。

我发现 locate_in_string 函数在这种情况下效果很好。

select substr(
              c, 
              locate_in_string(c, ',')+1, 
              locate_in_string(c, ',', locate_in_string(c, ',')+1) - locate_in_string(c, ',')-1
              ) as fruit2
  from ACL00 for read only with ur;