按组进行 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的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server - 找到最高计数后按组排序

在SQL中按组计算移动平均数

SQL 按组排序

SQL Server Rank() 按组

SQL:过去 30 天的滚动总和(按组)

SQL:按组划分的最大可能日期范围