2013-03-13 56 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