2015-09-17 71 views
0

問題通過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來工作)。 然而,由於原始代碼的其他部分寫得很糟糕,我對這種新技術感到擔憂,所以想在將這種方法添加到我的庫中之前,先看看專家對這種方法的看法。

+2

只有當CTE從單個表中選擇時,纔可以直接在CTE上使用'UPDATE','DELETE'或'INSERT'。否則,你必須加入它。 –

+2

沒有'ORDER BY'的'SELECT TOP 1 WorkOrderId'不是確定性的。 –

+2

@TimSchmelter - 對於'update'不是這樣,但更新的列必須全部來自同一個表。 –

回答

2

這太長了評論。

更新CTE很好。您可以使用的子查詢有一些限制(例如沒有聚合)。

但是,您對SQL Server中的CTE存在誤解。他們不創建內存表。相反,它們更像視圖,代碼包含在查詢中。整個查詢然後被優化。注意:這種行爲不同於其他數據庫和(據我所知),即使有提示,也沒有辦法覆蓋它。

這是一個重要的區別。如果你有一個複雜的CTE並且多次使用它,那麼它通常會針對整個查詢中的每個引用執行。

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視爲臨時視圖,因此我傾向於遵循Microsoft關於更新視圖的說法。請參閱this page上的可更新視圖部分。