按组进行 SQL UPDATE
Posted
技术标签:
【中文标题】按组进行 SQL UPDATE【英文标题】:SQL UPDATE by groups 【发布时间】:2016-06-04 20:09:50 【问题描述】:考虑下表:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Product](
[ProductID] [int] IDENTITY(1,1) NOT NULL,
[ProductCategory] [int] NOT NULL,
[ProductCategoryGuid] [uniqueidentifier] NULL,
CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED
(
[ProductID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Product] ON
GO
INSERT [dbo].[Product] ([ProductID], [ProductCategory], [ProductCategoryGuid]) VALUES (1, 2, NULL)
GO
INSERT [dbo].[Product] ([ProductID], [ProductCategory], [ProductCategoryGuid]) VALUES (2, 2, NULL)
GO
INSERT [dbo].[Product] ([ProductID], [ProductCategory], [ProductCategoryGuid]) VALUES (3, 2, NULL)
GO
INSERT [dbo].[Product] ([ProductID], [ProductCategory], [ProductCategoryGuid]) VALUES (4, 3, NULL)
GO
INSERT [dbo].[Product] ([ProductID], [ProductCategory], [ProductCategoryGuid]) VALUES (5, 4, NULL)
GO
INSERT [dbo].[Product] ([ProductID], [ProductCategory], [ProductCategoryGuid]) VALUES (6, 2, NULL)
GO
INSERT [dbo].[Product] ([ProductID], [ProductCategory], [ProductCategoryGuid]) VALUES (7, 3, NULL)
GO
INSERT [dbo].[Product] ([ProductID], [ProductCategory], [ProductCategoryGuid]) VALUES (8, 4, NULL)
GO
INSERT [dbo].[Product] ([ProductID], [ProductCategory], [ProductCategoryGuid]) VALUES (9, 4, NULL)
GO
SET IDENTITY_INSERT [dbo].[Product] OFF
GO
数据如下所示:
ProductID ProductCategory ProductCategoryGuid
1 2 NULL
2 2 NULL
3 2 NULL
4 3 NULL
5 4 NULL
6 2 NULL
7 3 NULL
8 4 NULL
9 4 NULL
我想要实现的是更新 [ProductCategoryGuid] 列,以便 [ProductCategory] 具有相同值的所有行在 [ProductCategoryGuid] 列中具有相同的 Guid 值
澄清一下:
Guid 值将使用 NEWID() 函数作为 UPDATE 查询的一部分生成
ProductID IN(1, 2, 3, 6) 的行将具有 Guid1
ProductID IN(4,7) 的行将具有 Guid2
ProductID IN(5,8,9) 的行将具有 Guid3
【问题讨论】:
这将是您应该在 productcategory-table 中完成的工作。如果您没有,请创建一个。 没有 ProductCategory 表。我知道这有点违反直觉,但实际任务更复杂,这是为 SO 呈现它的最佳方式。我将尝试澄清: Guid 值将使用 NEWID() 函数作为查询的一部分生成 ProductID IN(1, 2, 3, 6) 的行将具有 Guid1 ProductID IN(4,7) 的行将具有 Guid2 ProductID IN(5,8,9) 的行将具有 Guid3 博格丹答案的导数:UPDATE p SET p.ProductCategoryGuid=pc.GD FROM dbo.Product p INNER JOIN (SELECT DISTINCT p2.ProductCategory, NEWID() AS GD FROM dbo.Product p2) AS pc ON p.ProductCategory=pc.ProductCategory @JoeSchmoe:我不确定这个衍生解决方案是否 100% 安全。 SELECT 子句和选项的执行逻辑顺序告诉 DBMS 生成结果:从 SELECT 子句计算表达式并执行 DISTINCT。因此,很可能会为 dbo.Product 中的每一行生成一个“新”GUID (NEWID()),并且 DISTINCT 将尝试删除重复的行。在这一点上,由于 NEWID() 不太可能重复行。 【参考方案1】:我将使用以下脚本,该脚本使用表变量来存储不同类别的列表。同一个表变量有一个 GUID 列,其默认值为 NEWID()
函数。在脚本的末尾有一个UPDATE
语句,使用表变量作为源,dbo.Product
表作为目标:
DECLARE @Results TABLE (
[ProductCategory] [int] NOT NULL,
[ProductCategoryGuid] [uniqueidentifier] NOT NULL DEFAULT (NEWID())
)
INSERT @Results (ProductCategory)
SELECT DISTINCT p.ProductCategory
FROM dbo.Product p
UPDATE p
SET ProductCategoryGuid = r.ProductCategoryGuid
OUTPUT deleted.ProductCategoryGuid, inserted.ProductCategoryGuid
FROM dbo.Product p
INNER JOIN @Results r ON p.ProductCategory = r.ProductCategory
如果您不想看到旧值和新值,请评论 OUTPUT
子句。
更新:单语句解决方案(需要SQL2012+)
;WITH CteUpdateProduct
AS (
SELECT *, FIRST_VALUE(NewGUID) OVER(PARTITION BY ProductCategory ORDER BY ProductID) AS NewProductCategoryGuid
FROM (
SELECT p.*, NEWID() AS NewGUID
FROM dbo.Product p
) x
)
UPDATE CteUpdateProduct
SET ProductCategoryGuid = NewProductCategoryGuid
OUTPUT inserted.ProductID, inserted.ProductCategory, inserted.ProductCategoryGuid;
【讨论】:
谢谢,这行得通。现在纯粹出于学术原因,我很好奇这是否可以在单个更新语句中完成。 单一语句解决方案的主要问题是当这些类别具有多行时。在这种情况下,SQL Server 将“请求”同一类别两次或多次关联的 GUID。这意味着对于相同的类别,NEWID()
函数可以执行两次或更多次,从而导致差异。同一类别的 GUID。要实现这些 GUID,可以使用此处描述的解决方案:***.com/questions/13090037/…
@JoeSchmoe:请参阅第二个解决方案,尽管有人可能会争辩说不是 100% 安全。【参考方案2】:
WITH productCategories as (
SELECT DISTINCT ProductCategory
FROM product
), productCategoriesWithGuid as (
SELECT ProductCategory, NEWID() ProductCategoryGuid
From productCategories
)
UPDATE product
SET ProductCategoryGuid = pc.ProductCategoryGuid
FROM Product p
JOIN productCategoriesWithGuid pc on p.ProductCategory = pc.ProductCategory
此查询获取不同的 ProductCategories,
为它们中的每一个创建一个 GUID,
最后使用 GUID 更新产品表
一站式服务。
【讨论】:
这个解决方案已经过测试?主要问题是当有两行或多行具有相同类别时,JOIN 的内部将被评估多少次? 这对我不起作用。它为每一行生成唯一的 Guid。 这很奇怪。想不通为什么。会调查的。以上是关于按组进行 SQL UPDATE的主要内容,如果未能解决你的问题,请参考以下文章