我有一個包含500k行的表,其中地址在一個字段中,由Char(13)+ Char(10)分隔。我已經在表格中添加了5個字段,希望將其分開。在大型表中將地址列拆分爲多個
發現在線this split function,似乎表現良好,因爲我不能使用parsename
由於有5個部分,.
也可能在現場。
這是一個表值函數,所以我將不得不循環行和更新記錄,以前我會使用遊標或sql while或可能甚至c#做到這一點,但我覺得他們必須是一個cte或設置爲基礎的答案。
我有一個包含500k行的表,其中地址在一個字段中,由Char(13)+ Char(10)分隔。我已經在表格中添加了5個字段,希望將其分開。在大型表中將地址列拆分爲多個
發現在線this split function,似乎表現良好,因爲我不能使用parsename
由於有5個部分,.
也可能在現場。
這是一個表值函數,所以我將不得不循環行和更新記錄,以前我會使用遊標或sql while或可能甚至c#做到這一點,但我覺得他們必須是一個cte或設置爲基礎的答案。
所以給出了一些源數據:
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);
我現在試試這個,謝謝。感謝您的時間併爲此提供幫助。 – 2013-03-13 15:48:36
你有兩個選擇:
您可以創建一個臨時表,然後解析地址到臨時表,然後通過其連接到臨時表更新原表。
或
您可以編寫自己的T-SQL的功能和使用這些功能,在您的更新語句的功能類似如下:
UPDATE myTable
SET address1 = myGetAddress1Function(address),
address2 = myGetAddress2Function(address)....
我認爲挑戰是一個答案,(a)函數定義缺失,(b)創建5個不同的函數對我來說並不合適。 – 2013-03-13 15:40:02
還有很多其他選項。這些函數都沒有使用'PARSENAME':http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings – 2013-03-13 15:01:50
謝謝Aaron,我之前沒有遇到過你的帖子,你會同意嗎?詹姆斯的更新? – 2013-03-13 15:11:42
我正在研究只需要一個功能的完整答案。 – 2013-03-13 15:14:12