我正在使用SQL Server 2014。
我通过将所有物理地址列移动到专用地址表来反规范化现有数据库,如下所示:
Customers (CustomerId, AddressLine1, AddressLine2, City, State, Country, etc )
Employees (EmployeeId, AddressLine1, AddressLine2, City, State, Country, etc )
Orders (OrderId, AddressLine1, AddressLine2, City, State, Country, etc )
Addresses (AddressId, Street, City, State, Country, Zip )
Customers (CustomerId, AddressId, etc )
Employees (CustomerId, AddressId, etc )
Orders (CustomerId, AddressId, etc )
将每个表中的数据导入地址非常简单:
INSERT INTO Addresses (Street, City, State, Country)
SELECT
CONCAT(AddressLine1, AddressLine2), City, State, Country)
FROM Customers
UNION ALL
SELECT
CONCAT(AddressLine1, AddressLine2), City, State, Country)
FROM Employees
UNION ALL
SELECT
CONCAT(AddressLine1, AddressLine2), City, State, Country)
FROM Orders
但是,如何为每个插入的行获取新的AddressId
值并将其设置为Customers.AddressId
,Employees.AddressId
和Orders.AddressId
值?
...理想情况下,不使用获取单行的巨型循环,插入它,并使用SCOPE_IDENTITY
进行更新。
答案 0 :(得分:3)
假设
1)地址,客户,员工和订单具有以下PK:AdressId,CustomerId,EmployeeId,OrderId
和
2)dbo.Adresses.AdressId列(PK)具有IDENTITY属性
然后对于每个源表(客户等),您可以使用以下方法:
/*
-- Just once
CREATE TABLE #AffectedRows (
SourceID INT NOT NULL PRIMARY KEY,
AdressID INT NOT NULL
)
*/
SET XACT_ABORT ON
BEGIN TRAN
TRUNCATE TABLE #AffectedRows
MERGE dbo.Adresses a
USING dbo.Customers c ON a.AdressID = c.AdressID
WHEN NOT MATCHED
THEN
INSERT (City)
VALUES (c.City)
OUTPUT c.CustomerId, inserted.AdressId INTO #AffectedRows;
UPDATE c
SET c.AdressId = ar.AdressId
FROM dbo.Customers c
JOIN #AffectedRows ar ON c.CustomerId = ar.SourceId
--WHERE c.AdressId IS NULL
COMMIT
SELECT * FROM dbo.Customers
答案 1 :(得分:0)
这里有一个CURSOR
方法可行,但速度不是很快,需要为每个表格复制,我来自:
DECLARE @customerId int, @address1 nvarchar(200), @address2 nvarchar(200), @addressCity nvarchar(100), @addressState nvarchar(50), @addressZip nvarchar(50)
DECLARE c CURSOR FOR
SELECT
CustomerId,
AddressLine1,
AddressLine2,
AddressCity,
AddressState,
AddressZip
FROM
Customers
WHERE
LEN( AddressLine1 ) > 0 OR
LEN( AddressLine2 ) > 0 OR
LEN( AddressCity ) > 0 OR
LEN( AddressState ) > 0 OR
LEN( AddressZip ) > 0
OPEN c
FETCH NEXT FROM c INTO @customerId, @address1, @address2, @addressCity, @addressState, @addressZip
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO Addresses ( Street, City, State, PostalCode, Country )
SELECT
CASE WHEN LEN( @address2 ) > 0 THEN CONCAT( @address1, CHAR(13), CHAR(14), @address2 ) ELSE @address1 END,
@addressCity,
@addressState,
@addressZip,
'United States'
UPDATE Customers SET AddressId = SCOPE_IDENTITY() WHERE CustomerId = @customerId
FETCH NEXT FROM c INTO @customerId
END
CLOSE c
DEALLOCATE C
我之前只将@customerId
存储为变量并执行了SELECT (addressCols) FROM Customers WHERE CustomerId = @customerId
但性能更差(表格中包含所有必需的索引 - 所以它可能只是因为它正在做不必要的额外阅读。)
我很想知道是否有比这更好的方法。