1

同じテーブルから生成された値でテーブルを更新したいと考えています。

私の目標は、_NS や _WP などの proftxt と同じ ao を使用してすべての行を検索し、それらを合計し
、値をその ao の _H、_G、_L 要素の数で割り、この値を _H、_G に追加することです。その ao の _L オブジェクト。

ao に _NS 行と _WP 行しかない可能性があります。ルーチンがこの ao を飛び越える必要があります。

例:

私のデータは次のようになります。

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

ルーチンが非常に遅いことはわかっていますが、どうすればよいですか?

4

3 に答える 3

5

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あなたが与えたサンプルセットの場合、これはまったく同じ結果を生成します(タイプミスだと思われる列を除いて)。

完全な開示、これはサンプルセットでルーチンよりも時間がかかります (3 ミリ秒と比較して 17 ミリ秒) が、大規模なデータに大きくスケールアップする必要があります。私は正確さのためにそれをトランザクションに入れましたが、あなたの正確なユースケースが何であるかはわかりません. ただし、ルーチンにはトランザクションがありませんでした。これにより、データが正しくなくなる可能性があるため、この方法を維持する場合は、更新と削除の各ペアを必ず独自のトランザクションに配置してください。

また、 に索引がない場合はproftxt、追加してください。これは、両方のソリューションに大きな違いをもたらします。

幸運を。これが私が使用したSQLフィドルです

于 2013-05-06T15:24:33.107 に答える
1

私は両方の(本当に役に立ちました!)答えを組み合わせました。criticalfix が教えてくれたように、テーブルにインデックスを設定するための coloum プロファイルを追加しました。

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 分で実行されました!!!

于 2013-05-08T18:21:36.803 に答える