在SQL SELECT Query中替换偶数/奇数出现的字符串

时间:2015-02-24 19:57:26

标签: sql sql-server sql-server-2012

假设我在SQL Server 2012上运行此查询

SELECT Coordinates 
FROM Table

我得到了这个结果:

TextA,TextB,TextC,TextD,TextE,TextF

我想要的是解析这个结果并获得以下结果:

TextA,TextB;TextC,TextD;TextE,TextF

也就是说,每对后面都有一个分号。

有可能实现这一目标吗?

4 个答案:

答案 0 :(得分:1)

编辑1:我刚注意到结果的最后一行有问题(它也是一个X),现在尝试解决这个问题。

编辑2:修复了编辑1中的错误

此查询为您提供了一个函数,可以像这样结果分割您的输入字符串:

select * from dbo.Split('123,456,789,000', ',')

Nr  Typ  Value
1   X    123
1   Y    456
2   X    789
2   Y    000

功能码:

CREATE FUNCTION Split (
    @InputString    VARCHAR(5000),
    @Delimiter      VARCHAR(50)
    )

RETURNS @Items TABLE (
        Nr      INT,
        Typ     VARCHAR(50),
        Value   VARCHAR(MAX)
    )

AS
BEGIN
    IF @Delimiter = ' '
    BEGIN
        SET @Delimiter = ','
        SET @InputString = REPLACE(@InputString, ' ', @Delimiter)
    END

    IF (@Delimiter IS NULL OR @Delimiter = '')
        SET @Delimiter = ','

        DECLARE @Item       VARCHAR(5000)
        DECLARE @ItemList   VARCHAR(5000)
        DECLARE @Typ        VARCHAR(50)
        DECLARE @DelimIndex INT
        DECLARE @RowNumber  INT

        SET @ItemList = @InputString
        SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
        SET @Typ = 'Y'      -- the typ for the coordinates, also the indicator for @RowNumber
        SET @RowNumber = 0  -- each pair gets their own rownumber

        WHILE (@DelimIndex != 0)
        BEGIN
            SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex)
            SET @Typ = (CASE WHEN @Typ = 'Y' THEN 'X' ELSE 'Y' END)
            SET @RowNumber = @RowNumber + (CASE WHEN @Typ = 'X' THEN 1 ELSE 0 END)

            INSERT INTO @Items VALUES (@RowNumber, @Typ, @Item)

            SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)-@DelimIndex)
            SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
        END

        IF @Item IS NOT NULL -- if at least 1 delimiter was encountered in @InputString
        BEGIN
            SET @Item = @ItemList
            INSERT INTO @Items VALUES (@RowNumber + 1, (CASE WHEN @Typ = 'Y' THEN 'X' ELSE 'Y' END), @Item)
        END

        -- if 0 delimiters where found it just returns the default @InputString
        ELSE 
            INSERT INTO @Items VALUES (@RowNumber + 1, @Typ, @InputString)

        RETURN
    END
GO

答案 1 :(得分:1)

使用递归CTE,您可以快速轻松地完成此任务:

WITH Tab AS(
    SELECT ID = 1
         , Coordinates = 'TexasdasdtA,TextB,TextC,TexsdsdstD,TextE,TextF'
         , AnotherCol = 'Bla'
),
ParseCoordinates AS(
    SELECT
          ID 
        , result    = CAST('' AS VARCHAR(MAX))
        , remaining = Tab.Coordinates
    FROM Tab
    UNION ALL
    SELECT 
          ID 
        , result    = result +';'+ NextPart.Value
        , remaining = SUBSTRING(remaining, LEN(NextPart.Value)+2, LEN(remaining))
    FROM ParseCoordinates
        CROSS APPLY(
            SELECT Value = SUBSTRING(remaining, 1, ISNULL(NULLIF(CHARINDEX(',',remaining, CHARINDEX(',',remaining)+1)-1, -1), LEN(remaining)))
        ) AS NextPart
    WHERE remaining <> ''
)
SELECT T.ID
     , Coordinates = SUBSTRING(result, 2, LEN(result))
     , AnotherCol
FROM Tab AS T
    INNER JOIN ParseCoordinates AS PS
        ON T.ID = PS.ID        
WHERE remaining = ''

编辑:根据列的数量,A)通过CTE传递它们或B)仅通过CTE传递一个键,并将原始选项卡连接到具有解析值的CTE。

答案 2 :(得分:0)

这会做你想要的事情:

CREATE TABLE TestTable (Label varchar(20));

INSERT INTO TestTable (Label)
VALUES ('TextA'), ('TextB'), ('TextC'), ('TextD'), ('TextE'), ('TextF');

DECLARE @flag bit;
DECLARE @result varchar(MAX);
SET @flag = 0;
SET @result = ''; 
SELECT @result += Label + CASE WHEN @flag = 0 THEN ',' ELSE ';' END, @flag = ~@flag
FROM TestTable;
SELECT @result;

答案 3 :(得分:0)

创建此功能以对坐标进行分组。该功能已根据here进行了调整,其功能可根据您的要求进行扩展。

CREATE FUNCTION [dbo].[udf_CoordinatesGrouper] (
    @List VARCHAR(max)
    ,@delimiter VARCHAR(10)
    )
RETURNS @ReturnValue TABLE (col VARCHAR(8000))
AS
BEGIN
    DECLARE @Values TABLE (
        ID INT IDENTITY(1, 1)
        ,col VARCHAR(8000)
        )

    IF @List IS NULL
        OR LEN(@List) = 0
        RETURN;

    SET @List = replace(@List, CHAR(39) + CHAR(39), CHAR(39))

    DECLARE @Index INT = 1;
    DECLARE @ItemValue VARCHAR(100);
    DECLARE @pos INT = 1;
    DECLARE @l INT = LEN(@List);

    WHILE @Index > 0
    BEGIN
        SET @Index = CHARINDEX(@Delimiter, @List, @pos);

        IF @Index > 0
            IF (@index - @pos > 0)
                SET @ItemValue = SUBSTRING(@List, @pos, @index - @pos);
            ELSE
                SET @ItemValue = NULL;
        ELSE IF (@l - @pos + 1) > 0
            SET @ItemValue = SUBSTRING(@List, @pos, @l - @pos + 1);
        ELSE
            SET @ItemValue = NULL;

        INSERT INTO @Values (col)
        VALUES (@ItemValue);

        SET @pos = @index + 1;
    END;

    WITH cte_XAxis
    AS (
        SELECT id
            ,col
        FROM @Values
        WHERE id % 2 = 1
        )
        ,cte_YAxis
    AS (
        SELECT id
            ,col
        FROM @Values
        WHERE id % 2 = 0
        )
        --INSERT INTO @ReturnValue
        ,cte_Coordinates
    AS (
        SELECT xa.col + ISNULL(',' + ya.col, '') AS Coordinates
        FROM cte_XAxis xa
        LEFT JOIN cte_YAxis ya ON xa.ID = (ya.ID - 1)
        )
    INSERT INTO @ReturnValue
    SELECT STUFF((
                SELECT ';' + Coordinates
                FROM cte_Coordinates t2
                FOR XML PATH('')
                ), 1, 1, '')

    RETURN;
END
GO

用法

SELECT *
FROM [udf_CoordinatesGrouper]('TextA,TextB,TextC,TextD,TextE,TextF', ',')
GO