2013-05-06 3 views
1

같은 테이블에서 생성 된 값으로 테이블을 업데이트하고 싶습니다.업데이트/삭제 쿼리의 성능

내 목표는 _NS 및 _WP과 같은 AO와 같은 proftxt 모든 행을 검색을 요약,
나누기에게 값을 _H, _G, 그 AO의 _L - 요소의 수를 통해이 값을 추가하는 것입니다 해당 AO의 _H, _G 및 _L 개체에 추가합니다.

ao에는 _NS 및 _WP 행만있을 수 있습니다. 루틴이이 AO를 뛰어 넘어야합니다. testdata로 소량에 대한

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 

내 일상적인 작품 :

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 

내 데이터가 같아야합니다

예 : 같은

내 데이터 보인다.

업데이트 기능은 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 

나는 루틴이 매우 느리다는 것을 알고 있지만 어떻게해야합니까?

+1

테이블에 색인이 있습니까? – SkyDrive

+0

아니요, 연도가 내 기본 키입니다. 나는 그것을 검사 할 것이다 ... – soeren

답변

1

둘 다 (정말 도움이되었습니다!) 답변을 결합했습니다.

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 

다음 내 테이블을 편집하는 밥의 코드를 사용 : 나는 criticalfix가 나에게 말했다 같은 coloum의 proftype 테이블에 인덱스를 설정하려면 덧붙였다.

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 분을 달렸다! !!

4

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 열을 제외하고 (동일한 결과를 얻을 수 나는 오타라고 생각했다).

전체 공개로, 이는 샘플 세트에서 루틴 (3ms에 비해 17ms)보다 오래 걸리지 만 큰 데이터까지 확장해야합니다. 나는 정확성을 위해 트랜잭션에 넣었지 만 정확한 사용 사례가 무엇인지 모르므로 전체 시간 동안 페이지를 잠그고 전체 테이블로 확대 할 수 있으므로 내 방식의 단점이 될 수 있습니다. 루틴에는 어떤 트랜잭션도 없었으므로 잘못된 데이터가 될 수 있으므로 각 업데이트 - 삭제 쌍을 자신의 트랜잭션에 넣어야합니다.

또한 proftxt에 색인이없는 경우 하나를 추가하십시오! 이것은 두 가지 솔루션 모두에서 큰 차이를 만듭니다.

행운을 비네. 여기에 the SQL Fiddle I used이 있습니다.

+1

당신의 도움에 감사드립니다! 그것은 내 아이디어보다 훨씬 빠르고 빠르다. 완벽하게 작동합니다! :) – soeren

1

처음에는 몇 가지 NULL 관련 문제가 있습니다. 예를 들어, 내부 루프는 분명히 완료하기 전에 NULL이 될 @AO을 기다리고 있습니다 : 당신이없는 무언가에 @AO를 설정할 때

WHILE (@AO >1) 

이것은 작동하지만 읽기 어렵습니다, 아마도보다 명확한 논리를 작성하려고 할 것입니다.

다음으로,이 조건은 항상 거짓이 될 것입니다 :

OR proftxt = NULL 

NULL 값 자체가 동일하지 않습니다.이 조건을 테스트하려면 다음을 작성해야합니다.

OR proftxt IS NULL 

또한 NULL 값은 COUNT (proftxt)에서 생략됩니다. 다음 샘플 쿼리를 실행 해보십시오. "경고 : Null 값은 집계 또는 다른 SET 조작에 의해 제거됩니다."메시지와 함께 1을 리턴합니다.

SELECT COUNT(fieldname) FROM (SELECT 1 AS fieldname UNION SELECT NULL AS fieldname) AS tablename 

마지막으로, 최고의 와일드 카드가 LIKE 조건이 인덱스를 사용할 수 없기 때문에, 성능 문제 해결되지 않습니다 proftxt 열을 인덱싱. 성으로 알파벳순으로 나열된 전화 번호부와 같은 색인을 생각해 볼 수 있습니다. 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 값을 찾고 싶을 때마다 전체 테이블을 스캔한다는 것입니다.

+1

고마워요! 위에서 볼 수 있듯이 색인을 만들고 실수를 수정했습니다. 완벽하게 작동합니다! :) – soeren

+0

@ user2354590을 환영합니다! 내 대답이 도움이된다면, 당신은 그것을 upvote 줄 수 있습니다. – criticalfix