2015-03-13 42 views
1

我需要多值列分爲單值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 
+0

你應該張貼關於您曾經試圖您收到什麼錯誤,有什麼解決辦法的信息等 – 2015-03-13 19:32:29

+2

有很多的可以在這裏StackOverflow上「分割字符串」功能,即有一個字符串和返回一個表,其中每行是其中一個子字符串(您可以指定一個分隔符)。使用這些來創建表格,您可以將它們加入到原始表格中以獲得您想要的結果。 – pmbAustin 2015-03-13 19:45:26

+0

我看到的最大問題是最後兩行。所有'簡單'的解決方案都需要假設多值列中有相同數量的'字段'。這是一個很難的要求? – pmbAustin 2015-03-13 20:22:20

回答

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匹配的行進行過濾(排隊)。

+0

它實際上是「代字號」;) – shawnt00 2015-03-13 21:08:07

+0

拼寫修正:-) – pmbAustin 2015-03-13 21:11:04

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)