问题
我刚刚遇到过在CTE上使用更新语句的概念。 这似乎是一个很好的方法,但我之前没有看过它,我介绍它的背景(即在一些写得不好的代码中发现它)表明作者并不知道它们是什么这样做。 是否有人知道有任何理由不对CTE进行更新/在执行此操作时应该做出的任何考虑(假设CTE提供了一些好处;例如允许您更新任意数据子集)。
完整信息
我最近在我们的生产环境中发现了一些可怕的代码,其中有人明显在尝试更新单行或数据的方法。我整理了布局以使其可读,但保留了原始逻辑。
CREATE PROCEDURE [dbo].[getandupdateWorkOrder]
-- Add the parameters for the stored procedure here
-- @p1 xml Output
AS
BEGIN
WITH T AS
(
SELECT XMLDoc
, Retrieved
from [Transcode].[dbo].[WorkOrder]
where WorkOrderId IN
(
SELECT TOP 1 WorkOrderId
FROM
(
SELECT DISTINCT(WorkOrderId)
, Retrieved
FROM [Transcode].[dbo].[WorkOrder]
WHERE Retrieved = 0
) as c
)
AND Retrieved = 0
)
UPDATE T
SET Retrieved = 1
Output inserted.XMLDoc
END
我可以轻松地将其更新到下面,而不会影响逻辑:
CREATE PROCEDURE [dbo].[GetAndUpdateWorkOrder]
AS
BEGIN
WITH T AS
(
SELECT top 1 XMLDoc
, Retrieved
from [WorkOrder]
where Retrieved = 0
)
UPDATE T
SET Retrieved = 1
Output inserted.XMLDoc
END
然而,该代码还向我介绍了一个新概念;您可以更新CTE /查看基础表中的那些更新(我先前假设CTE只读取从原始表中选择的数据的内存副本,因此无法修改)。 如果我没有看到原始代码,但需要一些表现得像这样的东西,我已按如下方式编写:
CREATE PROCEDURE [dbo].[GetAndUpdateWorkOrder]
AS
BEGIN
UPDATE [WorkOrder]
SET Retrieved = 1
Output inserted.XMLDoc
where Id in
(
select top 1 Id
from [WorkOrder]
where Retrieved = 0
--order by Id --I'd have included this too; but not including here to ensure my rewrite is *exactly* the same as the original in terms of functionality, including the unpredictable part (the bonus of not including this is a performance benefit; though that's negligible given the data in this table)
)
END
通过CTE执行更新的代码看起来更清晰(即,您甚至不需要依赖唯一ID来实现此功能)。 然而,因为原始代码的其余部分写得很糟糕,我对这种新技术感到担忧,所以想要在将它添加到我的工具库之前看看专家对这种方法的看法。
答案 0 :(得分:2)
评论太长了。
更新CTE很好。您可以使用的子查询存在限制(例如没有聚合)。
但是,您对SQL Server中的CTE存在误解。它们不会创建内存表。相反,它们更像视图,其中代码包含在查询中。然后优化整体查询。注意:此行为与其他数据库不同(据我所知),即使有提示,也无法覆盖此行为。
这是一个重要的区别。如果您有一个复杂的CTE并且多次使用它,那么它通常会针对整个查询中的每个引用执行。
答案 1 :(得分:2)
通过CTE更新很好。当你必须处理窗口函数时,它特别方便。例如,您可以使用此查询为每个部门中排名前10位的员工提高10%:
WITH TopPerformers AS
(
SELECT DepartmentID, EmployeeID, Salary,
RANK() OVER (PARTITION BY DepartmentID ORDER BY PerformanceScore DESC) AS EmployeeRank
FROM Employees
)
UPDATE TopPerformers
SET Salary = Salary * 1.1
WHERE EmployeeRank <= 10
(我忽略了这样一个事实,即每个部门可以有超过10名员工,以防许多人获得相同的分数,但这超出了这一点。)
干净整洁,易于理解。我认为CTE是一个临时视图,所以我倾向于遵循微软关于更新视图的说法。请参阅this page上的“可更新视图”部分。