SQL Server多值列问题

时间:2015-03-13 19:26:27

标签: sql sql-server

我需要将多值列分成单个值

SOS_ID ALLOCATED_PART_NBR   ALLOCATED_SALES_ITM   ALLOCATED_QTY
523    500~5008~038~5008    2302~~007~5û005       1~1~~~1~2

注意:如果~分隔符之间没有值,则应插入空字符串。

我希望输出如下:

SOS_ID   ALLOCATED_PART_NBR  ALLOCATED_SALES_ITM    ALLOCATED_QTY
523      500                 2302                   1
523      5008                ''                     1
523      038                 007                    ''
523      5008                5û005                  ''
523      ''/NULL             ''/NULL                1
523      ''/NULL             ''/NULL                2

2 个答案:

答案 0 :(得分:1)

所以......这是我为你想要的东西而工作的一种方法。首先,您需要一个表值函数,它将字符串拆分为基于分隔符的字段,并将填充返回指定长度的行数:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[SplitString]') AND type IN (N'FN', N'IF', N'TF', N'FS', N'FT'))
    DROP FUNCTION [dbo].[SplitString]
GO

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[SplitString] ( 
        @delimitedString nvarchar(4000),
        @delimiter nvarchar(100),
        @padRows int 
        )
/**************************************************************************
DESCRIPTION: 
        Accepts a delimited string and splits it at the specified
        delimiter points.  Returns the individual items as a table data
        type with the ElementID field as the array index and the Element
        field as the data

PARAMETERS:
        @delimitedString  - The string to be split
        @delimiter        - String containing the delimiter where
                            delimited string should be split
        @padRows          - Any rows less than this value will be padded
                            with empty rows (NULL means no padding)

RETURNS:
        Table data type containing array of strings that were split with
        the delimiters removed from the source string

USAGE:
        SELECT ElementID, Element 
        FROM asi_SplitString('11111,22222,3333', ',', NULL) 
        ORDER BY ElementID

***************************************************************************/
RETURNS @tblArray TABLE 
   (
    ElementID int IDENTITY(1,1),
    Element nvarchar(1000)
   )
AS
BEGIN

    DECLARE @index int
    DECLARE @siStart int
    DECLARE @siDelSize int
    DECLARE @count int

    SET @count = 1;
    SET @siDelSize  = LEN(@delimiter);
    --loop through source string and add elements to destination table array
    WHILE LEN(@delimitedString) > 0
    BEGIN
        SET @index = CHARINDEX(@delimiter, @delimitedString);
        IF @index = 0
        BEGIN
            INSERT INTO @tblArray VALUES (@delimitedString);
            BREAK;
        END
        ELSE
        BEGIN
            INSERT INTO @tblArray VALUES(SUBSTRING(@delimitedString, 1,@index - 1));
            SET @siStart = @index + @siDelSize;
            SET @delimitedString = SUBSTRING(@delimitedString, @siStart , LEN(@delimitedString) - @siStart + 1);
        END
        SET @count += 1;
    END

    IF (@padRows IS NOT NULL)
        WHILE (@count < @padRows)
        BEGIN
            SET @count += 1;
            INSERT INTO @tblArray VALUES ('');
        END

    RETURN;
END

GO

现在你需要一个带有数据的样本表来测试这个(基于你的问题):

CREATE TABLE TestTable (SOS_ID nvarchar(10), 
                        ALLOCATED_PART_NBR nvarchar(400), 
                        ALLOCATED_SALES_ITM nvarchar(400), 
                        ALLOCATED_QTY nvarchar(400))

INSERT INTO TestTable (SOS_ID, ALLOCATED_PART_NBR, ALLOCATED_SALES_ITM, ALLOCATED_QTY)
VALUES ('523', '500~5008~038~5008', '2302~~007~5û005', '1~1~~~1~2')

现在,一些代码会将上面的数据转换为您想要的结果:

DECLARE @fieldCount int;
WITH TildeCounts AS (
    SELECT LEN(ALLOCATED_PART_NBR) - LEN(REPLACE(ALLOCATED_PART_NBR, '~', '')) AS TildeCount
      FROM TestTable t
    UNION ALL
    SELECT LEN( ALLOCATED_SALES_ITM) - LEN(REPLACE( ALLOCATED_SALES_ITM, '~', '')) AS TildeCount
      FROM TestTable t
    UNION ALL
    SELECT LEN(ALLOCATED_QTY) - LEN(REPLACE(ALLOCATED_QTY, '~', '')) AS TildeCount
      FROM TestTable t
) SELECT @fieldCount = MAX(TildeCount) + 1 FROM TildeCounts;

SELECT t.SOS_ID, a.Element AS [ALLOCATED_PART_NBR], b.Element AS [ALLOCATED_SALES_ITM], c.Element AS [ALLOCATED_QTY]
  FROM TestTable t
  CROSS APPLY dbo.SplitString(ALLOCATED_PART_NBR, '~', @fieldCount)  a
  CROSS APPLY dbo.SplitString(ALLOCATED_SALES_ITM, '~', @fieldCount)  b 
  CROSS APPLY dbo.SplitString(ALLOCATED_QTY, '~', @fieldCount)  c
WHERE a.ElementID = b.ElementID AND b.ElementID = c.ElementID  

这样做首先获得所有字符串中的最大字段数(因此它可以填充较短的字符串)。然后它从表中选择CROSS APPYING函数到每一列,仅过滤所有ID匹配的行(排队)。

答案 1 :(得分:1)

将字符串转换为xml,然后从每个节点中选择第n个节点。

SQL Fiddle Demo

DECLARE @max_field_count int = 6;

SELECT 
  SOS_ID
 ,ALLOCATED_PART_NBR  = CAST(N'<a>'+REPLACE(ALLOCATED_PART_NBR ,'~','</a><a>')+'</a>' AS XML).query('(a)[sql:column("i")]').value('.','varchar(max)')
 ,ALLOCATED_SALES_ITM = CAST(N'<a>'+REPLACE(ALLOCATED_SALES_ITM,'~','</a><a>')+'</a>' AS XML).query('(a)[sql:column("i")]').value('.','varchar(max)')
 ,ALLOCATED_QTY       = CAST(N'<a>'+REPLACE(ALLOCATED_QTY      ,'~','</a><a>')+'</a>' AS XML).query('(a)[sql:column("i")]').value('.','varchar(max)')
FROM MyTable
CROSS JOIN (SELECT TOP (@max_field_count) ROW_NUMBER() OVER(ORDER BY (SELECT 1)) FROM master.dbo.spt_values) n(i)