2013-03-13 101 views
0

我有一個包含500k行的表,其中地址在一個字段中,由Char(13)+ Char(10)分隔。我已經在表格中添加了5個字段,希望將其分開。在大型表中將地址列拆分爲多個

發現在線this split function,似乎表現良好,因爲我不能使用parsename由於有5個部分,.也可能在現場。

這是一個表值函數,所以我將不得不循環行和更新記錄,以前我會使用遊標或sql while或可能甚至c#做到這一點,但我覺得他們必須是一個cte設置爲基礎的答案。

+1

還有很多其他選項。這些函數都沒有使用'PARSENAME':http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings – 2013-03-13 15:01:50

+0

謝謝Aaron,我之前沒有遇到過你的帖子,你會同意嗎?詹姆斯的更新? – 2013-03-13 15:11:42

+0

我正在研究只需要一個功能的完整答案。 – 2013-03-13 15:14:12

回答

3

所以給出了一些源數據:

CREATE TABLE dbo.Addresses 
(
    AddressID INT IDENTITY(1,1), 
    [Address] VARCHAR(255), 
    Address1 VARCHAR(255), 
    Address2 VARCHAR(255), 
    Address3 VARCHAR(255), 
    Address4 VARCHAR(255), 
    Address5 VARCHAR(255) 
); 

INSERT dbo.Addresses([Address]) 
SELECT 'foo 
bar' 
UNION ALL SELECT 'add1 
add2 
add3 
add4 
add5'; 

我們來創建一個返回地址部分的函數:

CREATE FUNCTION dbo.SplitAddressOrdered 
(
    @AddressID INT, 
    @List  VARCHAR(MAX), 
    @Delimiter VARCHAR(32) 
) 
RETURNS TABLE 
AS 
    RETURN 
    (
     SELECT 
      AddressID = @AddressID, 
      rn = ROW_NUMBER() OVER (ORDER BY Number), 
      AddressItem = Item 
     FROM (SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(@List, Number, 
      CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number))) 
     FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id]) 
      FROM sys.all_objects) AS n(Number) 
     WHERE Number <= CONVERT(INT, LEN(@List)) 
     AND SUBSTRING(@Delimiter + @List, Number, LEN(@Delimiter)) = @Delimiter 
    ) AS y 
    ); 
GO 

現在你可以做到這一點(你將不得不運行查詢5次):

DECLARE 
    @i INT = 1, 
    @sql NVARCHAR(MAX), 
    @src NVARCHAR(MAX) = N';WITH x AS 
    (
     SELECT a.*, Original = s.AddressID, s.rn, s.AddressItem 
     FROM dbo.Addresses AS a 
     CROSS APPLY dbo.SplitAddressOrdered(a.AddressID, a.Address, 
     CHAR(13) + CHAR(10)) AS s WHERE rn = @i 
    )'; 
WHILE @i <= 5 
BEGIN 
    SET @sql = @src + N'UPDATE x SET Address' + RTRIM(@i) 
    + ' = CASE WHEN AddressID = Original AND rn = ' 
    + RTRIM(@i) + ' THEN AddressItem END;'; 

    EXEC sp_executesql @sql, N'@i INT', @i; 

    SET @i += 1; 
END 

然後你就可以刪除Address柱:

ALTER TABLE dbo.Addresses DROP COLUMN [Address]; 

那麼該表有:

AddressID Address1 Address2 Address3 Address4 Address5 
--------- -------- -------- -------- -------- -------- 
1   foo  bar  NULL  NULL  NULL 
2   add1  add2  add3  add4  add5 

我敢肯定有人比我更聰明,將展示如何利用該功能,而不必循環。

我也可以預見到的功能略有變化,將讓你簡單地拔出某一個元素......保持請...

編輯

這裏是一個標量函數,對自己更昂貴,但可以讓你做表的一個傳球,而不是5:

CREATE FUNCTION dbo.ElementFromOrderedList 
(
    @List  VARCHAR(MAX), 
    @Delimiter VARCHAR(32), 
    @Index  SMALLINT 
) 
RETURNS VARCHAR(255) 
AS 
BEGIN 
    RETURN 
    (
     SELECT Item 
     FROM (SELECT rn = ROW_NUMBER() OVER (ORDER BY Number), 
      Item = LTRIM(RTRIM(SUBSTRING(@List, Number, 
      CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number))) 
     FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id]) 
      FROM sys.all_objects) AS n(Number) 
     WHERE Number <= CONVERT(INT, LEN(@List)) 
     AND SUBSTRING(@Delimiter + @List, Number, LEN(@Delimiter)) = @Delimiter 
    ) AS y WHERE rn = @Index 
    ); 
END 
GO 

現在更新,給予上述(之前的更新和下降之前)上表,很簡單:

UPDATE dbo.Addresses 
    SET Address1 = dbo.ElementFromOrderedList([Address], CHAR(13) + CHAR(10), 1), 
     Address2 = dbo.ElementFromOrderedList([Address], CHAR(13) + CHAR(10), 2), 
     Address3 = dbo.ElementFromOrderedList([Address], CHAR(13) + CHAR(10), 3), 
     Address4 = dbo.ElementFromOrderedList([Address], CHAR(13) + CHAR(10), 4), 
     Address5 = dbo.ElementFromOrderedList([Address], CHAR(13) + CHAR(10), 5); 
+0

我現在試試這個,謝謝。感謝您的時間併爲此提供幫助。 – 2013-03-13 15:48:36

3

你有兩個選擇:

您可以創建一個臨時表,然後解析地址到臨時表,然後通過其連接到臨時表更新原表。

您可以編寫自己的T-SQL的功能和使用這些功能,在您的更新語句的功能類似如下:

UPDATE myTable 
    SET address1 = myGetAddress1Function(address), 
     address2 = myGetAddress2Function(address).... 
+0

我認爲挑戰是一個答案,(a)函數定義缺失,(b)創建5個不同的函數對我來說並不合適。 – 2013-03-13 15:40:02