[英]Performance on an update/delete query
我想用同一張表中生成的值更新我的表。
我的目標是搜索所有帶有proftxt的行,例如_NS和_WP,並使用相同的ao,將它們匯總,
將值除以該ao的_H,_G,_L-Elements的數量,然后將此值添加到該ao的_H,_G和_L對象中。
ao可能只有_NS和_WP行。 比例行程序應該跳過這一點。
例:
我的數據如下:
an, ao, proftxt, value, year
101 , 1, 'e_NSe', 5, 2006
102 , 1, 'e_Ha', 1, 2006
103 , 1, 'w_NSr', 4, 2006
104 , 2, 'w_NSr', 2, 2006
105 , 2, 'x_H05r', 4, 2006
106 , 2, 'w_Gr', 2, 2006
107 , 2, 'a_WPr', 4, 2006
108 , 3, 'a_WPr', 4, 2006
我的數據應該像:
an, ao, proftxt, value, year
102 , 1, 'e_Ha', 10 2006
103 , 2, 'x_H05r', 7, 2006
103 , 2, 'w_Gr', 5, 2006
108 , 3, 'a_WPr', 4, 2006
我的例程適用於少量的測試數據。
成功使用13個小時后,更新功能將在實際數據庫上運行時結束。
但是它僅編輯了210000行中的5000行。
DECLARE @ENDYEAR INT
DECLARE @AO BIGINT
DECLARE @YEAR INT
DECLARE @ELEMENTS INT
--Parameter festlegen
SET @YEAR = 2006
SET @ENDYEAR = 2013 --Endyear+1
SET @AO = 2
WHILE(@YEAR<@ENDYEAR)
BEGIN
WHILE (@AO >1) --Do as long as Cursor is inside table
BEGIN
SET @AO = (SELECT TOP 1 ao FROM tbl_slp -- Search ao with _WP _NS
WHERE (proftxt LIKE '%[_]WP%'
OR proftxt LIKE '%[_]NS%')
AND year = @YEAR
AND ao > @AO );
SET @ELEMENTS = (SELECT COUNT(proftxt) --Count Number of _H, _G, _L elements
FROM tbl_SLP
WHERE ao = @AO AND year = @YEAR AND
(proftxt LIKE '%[_]H%' OR proftxt = NULL
OR proftxt LIKE '%[_]G%'
OR proftxt LIKE '%[_]L%'))
IF (@ELEMENTS != 0)
BEGIN
UPDATE tbl_SLP --Update _H, _G, _L rows
SET value = value + (SELECT SUM(CONVERT(float, value))
FROM tbl_SLP
WHERE (proftxt LIKE '%[_]WP%'
OR proftxt LIKE '%[_]NS%')
AND year = @YEAR
AND ao = @AO)
/@ELEMENTS
WHERE ao = @AO AND year = @YEAR
DELETE FROM tbl_SLP --delete_WP _NS rows
WHERE ao= @AO
AND year = @YEAR
AND (proftxt LIKE '%[_]WP%' OR proftxt LIKE '%[_]NS%')
END
SET @AO = @AO +1
END
SET @YEAR = @YEAR +1
END
我知道例程非常慢,但是我該怎么辦?
SQL是為基於集合的操作而不是像例程一樣的過程控制流樣式邏輯而設計的。 這是一種基於集合的方法,我想它將比過程方法快得多:
SET XACT_ABORT ON
SET NOCOUNT ON
BEGIN TRANSACTION
-- Create a temp table with each ao-year's sums and counts (sums of _NS and _WP record values and counts of _H, _G, and _L records)
SELECT T.ao, T.year, SUM(T.value) AS SumVals, (SELECT COUNT(*) FROM tbl_slp A WHERE A.ao = T.ao AND A.year = T.year AND (A.proftxt = NULL OR A.proftxt LIKE '%[_]H%' OR A.proftxt LIKE '%[_]G%' OR A.proftxt LIKE '%[_]L%')) AS CountOther
INTO #temp1
FROM tbl_slp T
WHERE (T.proftxt LIKE '%[_]WP%' OR T.proftxt LIKE '%[_]NS%')
GROUP BY T.ao, T.year
-- Add "sum/count" for each ao-year to the _H, _G, and _L records for that year
UPDATE A
SET value = value + CONVERT(FLOAT, T.SumVals) / T.CountOther
FROM tbl_slp A
INNER JOIN #temp1 T ON A.ao = T.ao AND A.year = T.year
WHERE (A.proftxt = NULL OR A.proftxt LIKE '%[_]H%' OR A.proftxt LIKE '%[_]G%' OR A.proftxt LIKE '%[_]L%')
-- Now that we've distributed the _WP and _NS values, delete those records
DELETE A
FROM tbl_slp A
INNER JOIN #temp1 T ON A.ao = T.ao AND A.year = T.year
WHERE (A.proftxt LIKE '%[_]WP%' OR A.proftxt LIKE '%[_]NS%')
AND T.CountOther > 0
COMMIT TRANSACTION
對於您提供的樣本集,這將產生完全相同的結果(除了我認為是錯字的an
列)。
完全公開后,樣本集所需的時間比常規時間要長(17毫秒比3毫秒),但是它可以更好地擴展到大數據。 我將它放在事務中是為了確保正確性,但是我不確定您的確切用例是什么,因此這可能會不利於我的方式,因為它會在整個時間內鎖定頁面(並可能升級到整個表)。 但是,您的例行程序沒有任何事務,這可能會導致數據出錯,因此,如果您堅持自己的方式,請確保將每個更新刪除對放在自己的事務中。
另外,如果您在proftxt
上沒有索引,請添加一個! 這將對兩種解決方案都產生巨大的影響。
祝好運。 這是我使用的SQL Fiddle 。
首先,我看到一些與NULL相關的問題。 例如,您的內部循環顯然在等待@AO變為NULL才完成:
WHILE (@AO >1)
當您將@AO設置為不存在的東西時,這將起作用,但是很難閱讀,並且您可能想編寫更明確的邏輯。
接下來,此條件將始終為false:
OR proftxt = NULL
NULL值不等於其自身。 要測試這種情況,您必須編寫:
OR proftxt IS NULL
另外,您的COUNT(proftxt)中將忽略所有NULL值。 嘗試運行以下示例查詢。 它返回1,並顯示消息“警告:通過聚合或其他SET操作消除了空值”。
SELECT COUNT(fieldname) FROM (SELECT 1 AS fieldname UNION SELECT NULL AS fieldname) AS tablename
最后,為proftxt列建立索引不會解決您的性能問題 ,因為帶有前導通配符的LIKE條件無法使用該索引。 您可以想到索引,例如電話簿,按姓氏字母順序排列。 如果您正在尋找LastName LIKE'%mann',那么索引將無濟於事。 您仍然必須通讀電話簿中的每個條目,以找到所有以“ mann”結尾的姓氏。 用數據庫術語來說,這稱為“表掃描”,並且很慢。
我將添加一個新列,您可以將其稱為proftxttype。
UPDATE tbl_SLP
SET proftxttype = 1
WHERE proftxt LIKE '%[_]WP%'
OR proftxt LIKE '%[_]NS%'
UPDATE tbl_SLP
SET proftxttype = 2
WHERE proftxt LIKE '%[_]H%'
OR proftxt LIKE '%[_]G%'
OR proftxt LIKE '%[_]L%'
OR proftxt IS NULL
然后索引此列:
CREATE NONCLUSTERED INDEX [IX_PROFTXTTYPE] ON [dbo].[TBL_SLP] (PROFTXTTYPE ASC) ON [PRIMARY]
現在,根據proftxttype重寫您的更新。 當然,每當您插入或更新proftxt時,也將必須更新proftxttype。 這是不可避免的,但是SQL Server將負責使索引保持最新狀態,因此您不必擔心索引。
我知道這聽起來需要做很多工作,但是問題的核心在於,每當您要查找帶有前導通配符的proftxt值時,便要掃描整個表。
我結合了兩個(非常有幫助!)答案。 正如criticalfix告訴我的那樣,我添加了一個coloum proftype來在表上設置索引:
ALTER TABLE
ADD proftype CHAR(1)
GO
UPDATE tbl_SLPverrechnetWPNSP
SET proftype = 'W'
WHERE proftxt LIKE '%[_]WP%'
UPDATE tbl_SLP
SET proftype = 'N'
WHERE proftxt LIKE '%[_]NS%'
UPDATE tbl_SLP
SET proftype = 'H'
WHERE proftxt LIKE '%[_]H%'
OR proftxt IS NULL
UPDATE tbl_SLP
SET proftype = 'G'
WHERE proftxt LIKE '%[_]G%'
UPDATE tbl_SLP
SET proftype = 'L'
WHERE proftxt LIKE '%[_]L%'
--set index on proftype
CREATE NONCLUSTERED INDEX [IX_PROFTYPE] ON [dbo].[tbl_SLP] (proftype ASC) ON [PRIMARY]
GO
接下來,我使用bob中的代碼來編輯表。
SET XACT_ABORT ON
SET NOCOUNT ON
BEGIN TRANSACTION
-- Create a temp table with each ao-year's sums and counts (sums of N and W record values and counts of H, G, and L records)
SELECT T.ao, T.year, SUM(CONVERT(float, T.value)) AS SumVals, (SELECT COUNT(*)
FROM tbl_slp A
WHERE A.ao = T.ao
AND A.year = T.year
AND (A.proftype ='G' OR A.proftype = 'H' OR A.proftype = 'L' ))
AS CountOther
INTO #temp1
FROM tbl_slp T
WHERE (T.proftype = 'W' OR T.proftype = 'N')
GROUP BY T.ao, T.year
-- Add "sum/count" for each ao-year to the H, G, and L records for that year
UPDATE A
SET value = value + CONVERT(FLOAT, T.SumVals) / T.CountOther
FROM tbl_slp A
INNER JOIN #temp1 T ON A.ao = T.ao AND A.year = T.year
WHERE (A.proftype = 'H' OR A.proftype = 'G' OR A.proftype LIKE 'L')
-- Now that we've distributed the W and N values, delete those records
DELETE A
FROM tbl_slp A
INNER JOIN #temp1 T ON A.ao = T.ao AND A.year = T.year
WHERE (A.proftype = 'W' OR A.proftype = 'N')
AND T.CountOther > 0
DROP TABLE #temp1
COMMIT TRANSACTION
非常感謝你的幫助! 例行程序僅運行了3.5分鍾!!!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.