SQL合并更新问题

SQL Merge update issue

我在执行 SQL 合并操作时尝试增加项目代码

我有两个 table。一个有很多信息,包括客户和项目。我想将客户名称和项目名称从一个 table 合并到另一个。这篇文章很完美,向我展示了如何做我需要做的事情

https://www.mssqltips.com/sqlservertip/1704/using-merge-in-sql-server-to-insert-update-and-delete-at-the-same-time/

但是我需要维护一个项目编号,每次添加记录时该编号都会递增,而当您编辑客户或项目名称时则保留该编号。如果项目被删除,那么我们从下一个可用编号开始继续。我试图在分区上使用行号来做到这一点,但它没有给我正确数量的项目。

使用文章示例并提供可视化效果,我需要另一个名为 Type with Food or Drink 的列作为答案并得到

Item     Cost Code   Type
Tea      10    1     Drink
Coffee   12    2     Drink
Muffin   11    1     Food
Biscuit  4     2     Food

我将使用 link 提供的示例中的数据并添加更多数据以确保我涵盖所有情况所以首先让我们从这些 table 开始并填充他们。

--Create a target table
Declare @Products TABLE 
(
   ProductID INT PRIMARY KEY,
   ProductName VARCHAR(100),
   ProductNumber int,
   ProductType VARCHAR(100),
   Rate MONEY
) 

--Insert records into target table
INSERT INTO @Products
VALUES
   (1, 'Tea',       1,'Drink',10.00),
   (2, 'Coffee',    2,'Drink', 20.00),
   (3, 'BiscuitX1', 1,'Food', 45.00) ,
   (4, 'Muffin',    2,'Food', 30.00),
   (5, 'BiscuitX2', 3,'Food', 40.00),
   (6, 'BiscuitX3', 4,'Food', 45.00),
   (7, 'Donut',     5, 'Food', 30.00),
   (8, 'BiscuitX4', 6,'Food', 40.00),
   (9, 'BiscuitX5', 7,'Food', 45.00)  
--Create source table
Declare @UpdatedProducts TABLE 
(
   ProductID INT PRIMARY KEY,
   ProductName VARCHAR(100),
   ProductNumber int,
   ProductType VARCHAR(100),
   Rate MONEY
) 

--Insert records into source table
INSERT INTO @UpdatedProducts
VALUES
   (1, 'Tea',           0,'Drink', 10.00),
   (2, 'Coffee',        0,'Drink', 25.00),
   (4, 'Muffin',        0,'Food', 35.00),
   (7, 'Donut',         0, 'Food', 30.00),
   (10, 'Pizza',        0,'Food', 60.00),
   (11, 'PizzaLarge',   0,'Food', 80.00)

您可以看到我添加了 ProductNumber 和 ProductType。 对于@UpdatedProducts table 我假设你没有产品编号,如果你有那么你将毫无问题地进行直接合并,如果你没有你需要找到它。

所以让我们先更新@UpdatedProducts 中的 ProductNumber

;with cte as (
    select u.ProductID,u.ProductName,u.ProductType,u.Rate 
    ,coalesce(p.ProductNumber,row_number() over (partition by u.ProductType order by u.ProductID)
    +(select max(pp.ProductNumber) from @Products pp where pp.ProductType=u.ProductType)
    -(select Count(*) from @UpdatedProducts uu 
        inner join @Products ppp on ppp.ProductID=uu.ProductID
        where uu.ProductType=u.ProductType)) [ProductNumber]
    from @UpdatedProducts u
    left outer join @Products p on p.ProductID=u.ProductID
)
update a
    set a.[ProductNumber]=cte.[ProductNumber]
    From @UpdatedProducts a
        inner join cte on cte.ProductID=a.ProductID

我没有找到直接将其放入合并中的方法。

@UpdatedProducts更新后的结果如下:-

ProductID   ProductName ProductNumber   ProductType Rate
=========   =========== =============   =========== ====
1           Tea         1               Drink       10.00
2           Coffee      2               Drink       25.00
4           Muffin      2               Food        35.00
7           Donut       5               Food        30.00
10          Pizza       8               Food        60.00
11          PizzaLarge  9               Food        80.00

所以现在我们可以直接合并了,如下:-

--Synchronize the target table with refreshed data from source table
MERGE @Products AS TARGET
USING @UpdatedProducts AS SOURCE 
ON (TARGET.ProductID = SOURCE.ProductID) 
--When records are matched, update the records if there is any change
WHEN MATCHED AND TARGET.ProductName <> SOURCE.ProductName OR TARGET.Rate <> SOURCE.Rate 
    THEN UPDATE SET TARGET.ProductName = SOURCE.ProductName, TARGET.Rate = SOURCE.Rate ,TARGET.ProductNumber= TARGET.ProductNumber --left alone on edit
--When no records are matched, insert the incoming records from source table to target table
WHEN NOT MATCHED BY TARGET 
THEN INSERT (ProductID, ProductName, Rate,ProductNumber,ProductType) 
    VALUES (SOURCE.ProductID, SOURCE.ProductName, SOURCE.Rate,SOURCE.ProductNumber,SOURCE.ProductType)-- increments every time a record is added 
--When there is a row that exists in target and same record does not exist in source then delete this record target
WHEN NOT MATCHED BY SOURCE 
THEN DELETE 
--$action specifies a column of type nvarchar(10) in the OUTPUT clause that returns 
--one of three values for each row: 'INSERT', 'UPDATE', or 'DELETE' according to the action that was performed on that row
OUTPUT $action, 
DELETED.ProductID AS TargetProductID, 
DELETED.ProductName AS TargetProductName, 
DELETED.Rate AS TargetRate, 
INSERTED.ProductID AS SourceProductID, 
INSERTED.ProductName AS SourceProductName, 
INSERTED.Rate AS SourceRate; 


SELECT * FROM @Products

@Products 的结果如下:-

ProductID   ProductName ProductNumber   ProductType Rate
=========   =========== =============   =========== ====
1           Tea         1                Drink       10.00
2           Coffee      2                Drink       25.00
4           Muffin      2                Food        35.00
7           Donut       5                Food        30.00
10          Pizza       8                Food        60.00
11          PizzaLarge  9                Food        80.00

因为产品编号 (1,3,4,6,7) 都被跳过了,新的食品 Pizza 占据了产品编号 8,继续成为 PrizzaLarge 的产品 9。 希望这有帮助。