如何按列拆分T-SQL中的字符串?

时间:2015-08-27 16:01:25

标签: sql-server tsql sql-server-2008-r2

我知道这个问题已被问到很多,因为我无法找到合适的解决方案。

我有一个字符串的变体:

  

VALA |值Valb | 1 | val1a | Val1b | Val2a | Val2b | Val3a | Val3b

第3个元素介于0和3之间,并显示ValNaValNb之后的数量。当它为0时,之后没有元素。如果为1,则只有1个元素位于其后面(Val1aVal1b)。

我需要TSQL选择将每个元素作为列返回。例如:

  

SELECT valA,valB,1,val1a,Val1b

我不能使用PARSENAME因为它最多允许4个元素

以下是我所有组合的示例:

  

VALA |值Valb | 0

     

VALA |值Valb | 1 | val1a | Val1b

     

VALA |值Valb | 2 | val1a | Val1b | Val2a | Val2b

     

VALA |值Valb | 3 | val1a | Val1b | Val2a | Val2b | Val3a | Val3b

现在我正在使用此解决方案,但我无法管理最终元素:

DECLARE @param AS VARCHAR(MAX) = 'valA|ValB|2|val1a|val1b|val2a|val2b';
DECLARE @delimiter AS CHAR(1) = '|';

SELECT a FROM (VALUES (@param + @delimiter)) AS MyTable(a);

SELECT 
    CASE WHEN P1.Pos>0 THEN LEFT(Prm,P1.Pos-1) ELSE '' END AS valA, 
    CASE WHEN P2.Pos>0 THEN SUBSTRING (Prm, P1.Pos + 1, P2.Pos - P1.Pos - 1) ELSE '' END AS valB, 
    CASE WHEN P3.Pos>0 THEN SUBSTRING (Prm, P2.Pos + 1, P3.Pos - P2.Pos - 1) ELSE 0 END AS Num, 
    CASE WHEN (P4.Pos>0) AND (CASE WHEN P3.Pos>0 THEN SUBSTRING (Prm, P2.Pos + 1, P3.Pos - P2.Pos - 1) ELSE 0 END >= 1) THEN SUBSTRING (Prm, P3.Pos + 1, P4.Pos - P3.Pos - 1) ELSE '' END AS val1a, 
    CASE WHEN (P5.Pos>0) AND (CASE WHEN P3.Pos>0 THEN SUBSTRING (Prm, P2.Pos + 1, P3.Pos - P2.Pos - 1) ELSE 0 END >= 1) THEN SUBSTRING (Prm, P4.Pos + 1, P5.Pos - P4.Pos - 1) ELSE '' END AS val1b, 
    CASE WHEN (P6.Pos>0) AND (CASE WHEN P3.Pos>0 THEN SUBSTRING (Prm, P2.Pos + 1, P3.Pos - P2.Pos - 1) ELSE 0 END >= 2) THEN SUBSTRING (Prm, P5.Pos + 1, P6.Pos - P5.Pos - 1) ELSE '' END AS val2a, 
    CASE WHEN (P7.Pos>0) AND (CASE WHEN P3.Pos>0 THEN SUBSTRING (Prm, P2.Pos + 1, P3.Pos - P2.Pos - 1) ELSE 0 END >= 2) THEN SUBSTRING (Prm, P6.Pos + 1, P7.Pos - P6.Pos - 1) ELSE '' END AS val2b, 
    CASE WHEN (P8.Pos>0) AND (CASE WHEN P3.Pos>0 THEN SUBSTRING (Prm, P2.Pos + 1, P3.Pos - P2.Pos - 1) ELSE 0 END = 3) THEN SUBSTRING (Prm, P7.Pos + 1, P8.Pos - P7.Pos - 1) ELSE '' END AS val3a, 
    CASE WHEN (P9.Pos>0) AND (CASE WHEN P3.Pos>0 THEN SUBSTRING (Prm, P2.Pos + 1, P3.Pos - P2.Pos - 1) ELSE 0 END = 3) THEN SUBSTRING (Prm, P8.Pos + 1, P9.Pos - P8.Pos - 1) ELSE '' END AS val3b 
FROM 
    (VALUES (@param + @delimiter)) AS baseTable(Prm) 
    CROSS APPLY (SELECT (CHARINDEX(@delimiter, @param))) as P1(Pos) 
    CROSS APPLY (SELECT (CHARINDEX(@delimiter, @param, P1.Pos+1))) as P2(Pos) 
    CROSS APPLY (SELECT (CHARINDEX(@delimiter, @param, P2.Pos+1))) as P3(Pos) 
    CROSS APPLY (SELECT (CHARINDEX(@delimiter, @param, P3.Pos+1))) as P4(Pos) 
    CROSS APPLY (SELECT (CHARINDEX(@delimiter, @param, P4.Pos+1))) as P5(Pos) 
    CROSS APPLY (SELECT (CHARINDEX(@delimiter, @param, P5.Pos+1))) as P6(Pos) 
    CROSS APPLY (SELECT (CHARINDEX(@delimiter, @param, P6.Pos+1))) as P7(Pos) 
    CROSS APPLY (SELECT (CHARINDEX(@delimiter, @param, P7.Pos+1))) as P8(Pos) 
    CROSS APPLY (SELECT (CHARINDEX(@delimiter, @param, P8.Pos+1))) as P9(Pos) 

编辑:

我需要一种防弹解决方案,所以即使我的字符串为空,SQL也只返回空值。

请谁投票支持我的问题,你能否只是评论为什么?所以我可以记下来,不要再犯同样的错误。

1 个答案:

答案 0 :(得分:4)

测试数据

DECLARE @t TABLE (Value VARCHAR(8000))

INSERT INTO @t VALUES 
('valA|valB|0'),
('valA|valB|1|val1a|Val1b'),
('valA|valB|2|val1a|Val1b|Val2a|Val2b'),
('valA|valB|3|val1a|Val1b|Val2a|Val2b|Val3a|Val3b')

查询

;WITH Split_Fields (Field, xmlfields)
AS
(
    SELECT Value AS Field,
    CONVERT(XML,'<Fields><field>'  
    + REPLACE(Value,'|', '</field><field>') + '</field></Fields>') AS xmlfields
      FROM @t
)

 SELECT Field      
 ,xmlfields.value('/Fields[1]/field[1]','varchar(100)') AS Field1    
 ,xmlfields.value('/Fields[1]/field[2]','varchar(100)') AS Field2
 ,xmlfields.value('/Fields[1]/field[3]','varchar(100)') AS Field3    
 ,xmlfields.value('/Fields[1]/field[4]','varchar(100)') AS Field4
 ,xmlfields.value('/Fields[1]/field[5]','varchar(100)') AS Field5
 ,xmlfields.value('/Fields[1]/field[6]','varchar(100)') AS Field6
 ,xmlfields.value('/Fields[1]/field[7]','varchar(100)') AS Field7
 ,xmlfields.value('/Fields[1]/field[8]','varchar(100)') AS Field8
 ,xmlfields.value('/Fields[1]/field[9]','varchar(100)') AS Field9
 ,xmlfields.value('/Fields[1]/field[10]','varchar(100)') AS Field10
 FROM Split_Fields

结果

╔═════════════════════════════════════════════════╦════════╦════════╦════════╦════════╦════════╦════════╦════════╦════════╦════════╦═════════╗
║                      Field                      ║ Field1 ║ Field2 ║ Field3 ║ Field4 ║ Field5 ║ Field6 ║ Field7 ║ Field8 ║ Field9 ║ Field10 ║
╠═════════════════════════════════════════════════╬════════╬════════╬════════╬════════╬════════╬════════╬════════╬════════╬════════╬═════════╣
║ valA|valB|0                                     ║ valA   ║ valB   ║      0 ║ NULL   ║ NULL   ║ NULL   ║ NULL   ║ NULL   ║ NULL   ║ NULL    ║
║ valA|valB|1|val1a|Val1b                         ║ valA   ║ valB   ║      1 ║ val1a  ║ Val1b  ║ NULL   ║ NULL   ║ NULL   ║ NULL   ║ NULL    ║
║ valA|valB|2|val1a|Val1b|Val2a|Val2b             ║ valA   ║ valB   ║      2 ║ val1a  ║ Val1b  ║ Val2a  ║ Val2b  ║ NULL   ║ NULL   ║ NULL    ║
║ valA|valB|3|val1a|Val1b|Val2a|Val2b|Val3a|Val3b ║ valA   ║ valB   ║      3 ║ val1a  ║ Val1b  ║ Val2a  ║ Val2b  ║ Val3a  ║ Val3b  ║ NULL    ║
╚═════════════════════════════════════════════════╩════════╩════════╩════════╩════════╩════════╩════════╩════════╩════════╩════════╩═════════╝