如何根据列中特殊字符串的位置获取值

时间:2016-12-14 12:14:16

标签: sql-server

我有一张这样的表

Col1                    Col2
Adam                    1
Barbara, Catherina      1
Barbara, Catherina      2
Adam, Catherina         1
Adam, Catherina         2
Barbara, Adam, Daniela  1
Barbara, Adam, Daniela  2
Barbara, Adam, Daniela  3

需要一个select语句来填充Col3,如下所示

Col1                   Col2 Col 3
Adam                    1   Adam
Barbara, Catherina      1   Barbara
Barbara, Catherina      2   Catherina
Adam, Catherina         1   Adam
Adam, Catherina         2   Catherina
Barbara, Adam, Daniela  1   Barbara
Barbara, Adam, Daniela  2   Adam
Barbara, Adam, Daniela  3   Daniela

我尝试过使用SUBSTRING& CHARINDEX但无法解决问题。需要一个SQL,我们可以传递Col2值并填充Col3。

请帮忙。 谢谢你,维克拉姆

3 个答案:

答案 0 :(得分:3)

使用Parse / Split功能和Cross Apply。

我应该补充一点,如果你不能使用或不想要UDF,那么解析可以很容易地迁移到CROSS APPLY中。

Declare @YourTable table (Col1 varchar(25),Col2 int)
Insert Into @YourTable Values
('A',      1),
('B,C',    1),
('B,C',    2),
('A,C',    1),
('A,C',    2),
('B,A,D',  1),
('B,A,D',  2),
('B,A,D',  3)

Select A.*
      ,Col3 = B.RetVal
 From  @YourTable A
 Cross Apply (Select * from [dbo].[udf-Str-Parse](A.Col1,',') where RetSeq=A.Col2) B

返回

Col1    Col2    Col3
A       1       A
B,C     1       B
B,C     2       C
A,C     1       A
A,C     2       C
B,A,D   1       B
B,A,D   2       A
B,A,D   3       D

UDF(如果需要)

CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (  
    Select RetSeq = Row_Number() over (Order By (Select null))
          ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
    From (Select x = Cast('<x>'+ Replace(@String,@Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i)
);
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
  

只是为了好玩,这是NON-UDF版本

Select A.*
      ,Col3 = B.RetVal
 From  @YourTable A
 Cross Apply (
                Select * From( 
                    Select RetSeq = Row_Number() over (Order By (Select null))
                          ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
                    From (Select x = Cast('<x>'+ Replace(A.Col1,',','</x><x>')+'</x>' as xml).query('.')) as A 
                    Cross Apply x.nodes('x') AS B(i)
                ) C where RetSeq=A.Col2
             ) B 

答案 1 :(得分:1)

此版仅适用于SQL 2016和Azure SQL DB(more on STRING_SPLIT

 DECLARE @DaTable TABLE (Col1 varchar(32),Col2 int)
INSERT INTO @DaTable 
VALUES
('A',      1),
('B,C',    1),
('B,C',    2),
('A,C',    1),
('A,C',    2),
('B,A,D',  1),
('B,A,D',  2),
('B,A,D',  3)

SELECT S.*
      ,Col3 = T.Value
 From  @DaTable S
 Cross Apply (SELECT * FROM 
 (Select Value, Row_Number()  OVER (ORDER BY (SELECT 0)) AS RowNum from STRING_SPLIT(S.Col1,',')) AS ss 
 where ss.RowNum = S.Col2) AS T

Azure SQL DB注意事项:

  

STRING_SPLIT功能仅在兼容级别130下可用。[...]   请注意,即使在新的Azure SQL数据库中,兼容级别120也可能是默认的。

答案 2 :(得分:-1)

您可以创建一个获取字符串(col1)和文本索引(col2)的函数,并返回正确的值。查询将如下所示:

SELECT col1, col2, GetElement(co1, col2) as col3
FROM table

GetElement可以使用WHILE循环来查找值。伪代码:

index = 1
while index < col2
charindex of the ','
if index == col2 substring to the next ',' and return
else index = index + 1 and remove the substring from beginning to the charindex of ','