使用SQL Server

时间:2018-06-24 00:47:08

标签: sql sql-server sql-server-2016

我只想从定界字符串中提取最后三个值,并用这三个值生成定界子字符串。任何人都可以建议什么是最好的方法。我尝试使用STRING_SPLIT并能够成功地将字符串拆分为多个值,但是我不确定如何继续进行。任何想法都将不胜感激。

SELECT value FROM  STRING_SPLIT('CatalogTypeCode VARCHAR(10) NOT NULL
        ,EventID INT NOT NULL
        ,ModelCode TINYINT NOT NULL
        ,YearID INT NOT NULL
        ,PerilSetCode INT NOT NULL

        ,GrossLoss FLOAT  NULL
        ,GrossSD FLOAT  NULL
        ,GrossMaxLoss FLOAT  NULL',',')

输出:

CatalogTypeCode VARCHAR(10) NOT NULL

EventID INT NOT NULL

ModelCode TINYINT NOT NULL

YearID INT NOT NULL

PerilSetCode INT NOT NULL


GrossLoss FLOAT  NULL

GrossSD FLOAT  NULL

GrossMaxLoss FLOAT  NULL

预期输出:

'GrossLoss FLOAT  NULL,GrossSD FLOAT  NULL,GrossMaxLoss FLOAT  NULL'

3 个答案:

答案 0 :(得分:2)

也许另一个选择是与XML ...一起使用一点reverse() ...两次

示例

Declare @YourTable table (ID int,SomeCol varchar(150))
Insert Into @YourTable values 
 (1,'Val1,Val2,Val3,Val4')
,(2,'Val1,Val2,Val3,Val4,Val5,Val6')
,(3,'Val1,Val2')
,(4,'Val1')
,(5,null)

Select A.ID
      ,LastThree = reverse(concat(Pos1,','+Pos2,','+Pos3))
 From  @YourTable A
 Cross Apply (
                 Select Pos1 = n.value('/x[1]','varchar(max)')
                       ,Pos2 = n.value('/x[2]','varchar(max)')
                       ,Pos3 = n.value('/x[3]','varchar(max)')
                  From  (Select cast('<x>' + replace(reverse(SomeCol),',','</x><x>')+'</x>' as xml) as n) X
             ) B

返回

ID  LastThree
1   Val2,Val3,Val4
2   Val4,Val5,Val6
3   Val1,Val2        -- Notice only 2 values
4   Val1             -- Notice only 1 value
5                    -- Notice value was null

答案 1 :(得分:0)

不幸的是,split_string()不会返回值在字符串中的位置。

这将起作用,假设各行之间没有重复:

SELECT string_agg(line, ',') within group (order by pos) as lines_3
FROM (SELECT TOP (3) s.line, CHARINDEX(line, lines) as pos
      FROM (VALUES ('CatalogTypeCode VARCHAR(10) NOT NULL
            ,EventID INT NOT NULL
            ,ModelCode TINYINT NOT NULL
            ,YearID INT NOT NULL
            ,PerilSetCode INT NOT NULL

            ,GrossLoss FLOAT  NULL
            ,GrossSD FLOAT  NULL
            ,GrossMaxLoss FLOAT  NULL')
         ) v(lines) OUTER APPLY
         STRING_SPLIT(v.lines, ',') s(line)
      ORDER BY pos
     ) s

编辑:

糟糕,以上内容适用于SQL Server 2017,但不适用于2016。您可以使用条件聚合:

SELECT (MAX(CASE WHEN seqnum = 1 THEN line END) + ','
        MAX(CASE WHEN seqnum = 2 THEN line END) + ','
        MAX(CASE WHEN seqnum = 3 THEN line END)
       ) as lines_3
FROM (SELECT TOP (3) s.line,
             ROW_NUMBER() OVER (ORDER BY CHARINDEX(line, lines)) as seqnum
      FROM (VALUES ('CatalogTypeCode VARCHAR(10) NOT NULL
            ,EventID INT NOT NULL
            ,ModelCode TINYINT NOT NULL
            ,YearID INT NOT NULL
            ,PerilSetCode INT NOT NULL

            ,GrossLoss FLOAT  NULL
            ,GrossSD FLOAT  NULL
            ,GrossMaxLoss FLOAT  NULL')
         ) v(lines) OUTER APPLY
         STRING_SPLIT(v.lines, ',') s(line)
      ORDER BY pos
     ) s;

答案 2 :(得分:0)

我使用这种表值函数已有多年了。工作良好。创建函数后,请select top 3 * from lma.dbo.split_test('a,b,c,d,e',',') order by id desc

CREATE FUNCTION [dbo].[Split]
(
  @String VARCHAR(MAX),
  @Delimiter VARCHAR(10)
)       
RETURNS @Temptable TABLE (id int identity, items varchar(MAX))       
AS       
BEGIN   

DECLARE @Idx INT       
DECLARE @Slice VARCHAR(MAX)       
DECLARE @Delimiterlen INT=LEN(@Delimiter)
        --,@String VARCHAR(MAX)='N'
        --,@Delimiter VARCHAR(10)=';;'

IF (@String like '%' + @Delimiter + '%')
    BEGIN

        SELECT @Idx = 1       
            IF LEN(@String)<@Delimiterlen or @String is null  RETURN       

        WHILE @Idx!= 0       
        BEGIN       
            SET @Idx = CHARINDEX(@Delimiter,@String)       
            IF @Idx!=0       
                SET @Slice = left(@String,@idx-1)       
            ELSE       
                SET @Slice = @String       

            IF(LEN(@Slice)>0)  
                INSERT INTO @Temptable(Items) values(@Slice)       

            --290913 : IF WE WANT TO USE DELIMETER LENGTH GREATER THAN 2
            IF LEN(@String) >= (@Idx -1 + @Delimiterlen)
                SET @String = RIGHT(@String,LEN(@String) - (@Idx-1+@Delimiterlen))       
            IF LEN(@String) = 0 BREAK
        END   
    END

ELSE
    BEGIN
        INSERT INTO @Temptable(Items) values(@String) 
    END


DELETE @Temptable WHERE items =''

RETURN       
END