문제

쿼리에 사용되는 루프를 통해 17 의 수백만에 레코드를 제거하는 중복 터 지금까지에 대해 16 시간 과 나는 알고 싶어하는 경우 쿼리를 중지한 경우 지금 그것을 마무리 삭제 진술 또는 경우에는 삭제를 실행하는 동안 이 query?는 경우에,실제로 나는 중지 그것을 마무리를 삭제하거나 롤백?

내가 찾는 것 때 나는

 select count(*) from myTable

행 반환하는 것(이 일을하는 동안 쿼리)를 약 5 보다 무엇을 시작하는 줄 수를 했습니다.분명히 서버 자원은 매우 그래서 가난 것을 의미하지는 이 과정을 촬영 하고있 16 시간을 찾아 5 복제(있을 때 실제로 수천),이 실행될 수도 있습일까요?

이 쿼리했 6 초 2000 의 행 테스트 데이터를,그리고 그것은 훌륭한 작품에서는 설정 데이터,그래서 난 것이라고 생각 15 시간 수행에 대한 완전한 설정합니다.

어떤 아이디어가?

아래의 쿼리:

--Declare the looping variable
DECLARE @LoopVar char(10)


    DECLARE
     --Set private variables that will be used throughout
      @long DECIMAL,
      @lat DECIMAL,
      @phoneNumber char(10),
      @businessname varchar(64),
      @winner char(10)

    SET @LoopVar = (SELECT MIN(RecordID) FROM MyTable)

    WHILE @LoopVar is not null
    BEGIN

      --initialize the private variables (essentially this is a .ctor)
      SELECT 
        @long = null,
        @lat = null,
        @businessname = null,
        @phoneNumber = null,
        @winner = null

      -- load data from the row declared when setting @LoopVar  
      SELECT
        @long = longitude,
        @lat = latitude,
        @businessname = BusinessName,
        @phoneNumber = Phone
      FROM MyTable
      WHERE RecordID = @LoopVar

      --find the winning row with that data. The winning row means 
      SELECT top 1 @Winner = RecordID
      FROM MyTable
      WHERE @long = longitude
        AND @lat = latitude
        AND @businessname = BusinessName
        AND @phoneNumber = Phone
      ORDER BY
        CASE WHEN webAddress is not null THEN 1 ELSE 2 END,
        CASE WHEN caption1 is not null THEN 1 ELSE 2 END,
        CASE WHEN caption2 is not null THEN 1 ELSE 2 END,
        RecordID

      --delete any losers.
      DELETE FROM MyTable
      WHERE @long = longitude
        AND @lat = latitude
        AND @businessname = BusinessName
        AND @phoneNumber = Phone
        AND @winner != RecordID

      -- prep the next loop value to go ahead and perform the next duplicate query.
      SET @LoopVar = (SELECT MIN(RecordID) 
    FROM MyTable
    WHERE @LoopVar < RecordID)
    END
도움이 되었습니까?

해결책

아니요, SQL Server는 쿼리 실행을 중지하면 이미 수행 한 삭제를 롤백하지 않습니다. Oracle은 명시적인 행동 쿼리가 필요하거나 데이터가 롤백되지만 MSSQL은 아닙니다.

SQL Server를 사용하면 트랜잭션의 맥락에서 구체적으로 실행되거나 거래가 롤백되거나 트랜잭션이 커밋되지 않고 연결이 닫히지 않으면 롤백되지 않습니다. 그러나 위의 쿼리에는 트랜잭션 컨텍스트가 표시되지 않습니다.

또한 쿼리를 다시 구조화하여 삭제를 좀 더 효율적으로 만들 수 있지만, 본질적으로 상자 사양이 스너프에 올라가지 않으면 기다릴 수 있습니다.

앞으로, 당신은 테이블에 고유 한 색인을 만들어 다시이 문제를 해결하지 못하게해야합니다.

다른 팁

귀하의 쿼리가 포장되지 않습 트랜잭션에서,그래서 그것은 없 rollback 변경 내용은 이미 만들에 의해 개인이 삭제 문입니다.

저는 특별히 이 테스트는 자신에 자신의 SQL 서버를 사용하여 다음과 같은 쿼리,그리고 ApplicationLog 테이블이 비어 있었더라도 나는 취소 쿼리:

declare @count int
select @count = 5
WHILE @count > 0
BEGIN
  print @count
  delete from applicationlog;
  waitfor time '20:00';
  select @count = @count -1
END

그러나 당신의 쿼리 가능성이 있고 많은 일 또는 몇 주 동안,많은 더 이상 다음 15 시간입니다.예상할 수 있는 프로세스는 2000 년 레코드의 모든 6 초기 때문에 잘못된 각 반복하는 동안 반복이 상당히 오래 걸릴 수로 17 만원의 행 다음과 2000 년의 행이 있습니다.그렇지 않으면 쿼리 훨씬 적은 다음 두 번째로 2000 행고,그것을 일에 대한 모든 17million.

를 요청해야 새로운 질문에 당신이 어떻게 삭제할 수 있습니다 중복 행 효율적으로 합니다.

거래에 대해 명시적인 일을하지 않으면 연결이 자동 커밋 트랜잭션 방법. 이 모드에서는 모든 SQL 문이 트랜잭션으로 간주됩니다.

문제는 이것이 개별 SQL 문이 트랜잭션임을 의미하는지 여부입니다. 따라서 당신이 갈 때, 또는 외부 루프가 트랜잭션으로 간주되는지 여부입니다.

while 구성에 대한 설명에서 이것에 대한 어떤 논의도없는 것 같습니다. MSDN. 그러나 잠시 설명은 데이터베이스를 직접 수정할 수 없기 때문에 논리적으로 보일 것입니다. 그렇지 않습니다 자동 커밋 트랜잭션을 시작하십시오.

암시 적 거래

'암시 적 트랜잭션'이 설정되지 않은 경우 루프의 각 반복이 변경 사항을 커밋했습니다.

모든 SQL 서버를 '암시 적 트랜잭션'으로 설정할 수 있습니다. 이것은 데이터베이스 설정입니다 (기본적으로 꺼짐). 또한 관리 스튜디오 내부의 특정 쿼리 (Query Pane> 옵션을 마우스 오른쪽 버튼으로 클릭), 클라이언트의 기본 설정 또는 세트 명령문의 특정 쿼리 속성에 암시 적 트랜잭션을 가질 수도 있습니다.

SET IMPLICIT_TRANSACTIONS ON;

어느 쪽이든,이 경우 쿼리 실행의 중단에 관계없이 명시 적 커밋/롤백을 실행해야합니다.


암시 적 거래 참조 :

http://msdn.microsoft.com/en-us/library/ms188317.aspx

http://msdn.microsoft.com/en-us/library/ms190230.aspx

SQL에서 귀하와 같은 논리가있는 시스템을 상속했습니다. 우리의 경우, 우리는 유사한 이름/주소 등을 가진 퍼지 매칭을 사용하여 행을 연결하려고 노력했으며, 그 논리는 순전히 SQL로 수행되었습니다. 내가 그것을 물려 받았을 때 우리는 테이블에 약 30 만 행을 가졌으며 타이밍에 따라, 우리는 그들 모두와 일치하는 데 1 년이 걸릴 것이라고 계산했습니다.

SQL 이외의 외부에서 얼마나 빨리 수행 할 수 있는지 실험하면서 DB 테이블을 플랫 파일에 버리고 플랫 파일을 C ++ 프로그램에 읽고, 고유 한 인덱스를 작성하고, 퍼지 로직을 수행하는 프로그램을 작성했습니다. 그런 다음 플랫 파일을 데이터베이스에 다시 가져옵니다. SQL에서 1 년이 걸리는 것은 C ++ 앱에서 약 30 초가 걸렸습니다.

제 조언은 SQL에서하고있는 일조차 시도조차하지 마십시오. 수출, 프로세스, 재 입자.

이 시점까지 수행 된 삭제는 롤백되지 않습니다.


The의 원래 저자로 문제의 코드, 그리고 성능이 인덱스에 의존 할 것이라는 경고를 발행 한 후에는이를 속도를 높이기 위해 다음 항목을 제안합니다.

레코드는 기본 키가 더 좋습니다. 나는 정체성을 의미하지 않고 기본 키를 의미합니다. SP_HELP를 사용하여 확인하십시오

일부 인덱스는이 쿼리를 평가하는 데 사용해야합니다. 이 네 개의 열 중 어느 것이 가장 반복되는지, 색인이 무엇인지 알아냅니다.

SELECT *
FROM MyTable
WHERE @long = longitude
  AND @lat = latitude
  AND @businessname = BusinessName
  AND @phoneNumber = Phone

이 색인을 추가하기 전후에 쿼리 계획을 확인하여 인덱스 스캔이 추가되었는지 확인하십시오.

루프로서 쿼리는 적절한 인덱스를 사용하더라도 잘 확장하는 데 어려움을 겪게됩니다. 쿼리는 제안에 따라 단일 진술로 다시 작성해야합니다. 이전 질문 이에.

거래 내에서 명시 적으로 실행하지 않는 경우 실행 명령문 만 롤백합니다.

커서를 사용하여 단일 패스 알고리즘을 사용하여 다시 작성된 경우이 쿼리가 훨씬 더 효율적이라고 생각합니다. 경도, 위도, 비즈니스 이름 및 @phoneNumber로 커서 테이블을 주문할 것입니다. 한 번에 하나씩 줄을 밟을 것입니다. 행에 경도, 위도, 사업가 이름 및 PhoneNumber가 이전 행과 동일한 경우 삭제하십시오.

방법론을 진지하게 고려해야한다고 생각합니다. 세트에서 생각을 시작해야합니다 (성능을 위해서는 배치 처리가 필요하지만 1,700 만 레코드 테이블에 대해 행별로 행하지는 않습니다.)

먼저 모든 레코드에 복제가 있습니까? 나는 당신이 가장 먼저해야 할 일은 복제 된 레코드로만 처리하는 것입니다. 이것은 큰 테이블이며 다른 처리가 진행되는 일에 따라 시간이 지남에 따라 배치로 삭제를 수행해야 할 수도 있으므로 먼저 처리하려는 레코드를 자신의 테이블로 가져 와서 색인합니다. 다른 현명한 데이터베이스에서 테이블을 만들고 끝에서 떨어지지 않고 동시에이 모든 작업을 수행 할 수있는 경우 임시 테이블을 사용할 수도 있습니다.

같은 것 (참고 INDEX Statments를 작성하지 않았다. 나는 당신이 직접 찾을 수 있다고 생각한다) :

SELECT min(m.RecordID), m.longitude, m.latitude, m.businessname, m.phone  
     into  #RecordsToKeep    
FROM MyTable   m
join 
(select longitude, latitude, businessname, phone
from MyTable
group by longitude, latitude, businessname, phone
having count(*) >1) a 
on a.longitude = m.longitude and a.latitude = m.latitude and
a.businessname = b.businessname and a.phone = b.phone 
group by  m.longitude, m.latitude, m.businessname, m.phone   
ORDER BY CASE WHEN m.webAddress is not null THEN 1 ELSE 2 END,        
    CASE WHEN m.caption1 is not null THEN 1 ELSE 2 END,        
    CASE WHEN m.caption2 is not null THEN 1 ELSE 2 END



while (select count(*) from #RecordsToKeep) > 0
begin
select top 1000 * 
into #Batch
from #RecordsToKeep

Delete m
from mytable m
join #Batch b 
        on b.longitude = m.longitude and b.latitude = m.latitude and
        b.businessname = b.businessname and b.phone = b.phone 
where r.recordid <> b.recordID

Delete r
from  #RecordsToKeep r
join #Batch b on r.recordid = b.recordid

end

Delete m
from mytable m
join #RecordsToKeep r 
        on r.longitude = m.longitude and r.latitude = m.latitude and
        r.businessname = b.businessname and r.phone = b.phone 
where r.recordid <> m.recordID

또한 중복 행을 제거하는 다른 방법을 생각해보십시오.

delete t1 from table1 as t1 where exists (
    select * from table1 as t2 where
        t1.column1=t2.column1 and
        t1.column2=t2.column2 and
        t1.column3=t2.column3 and
        --add other colums if any
        t1.id>t2.id
)

테이블에 정수 ID 열이 있다고 생각합니다.

컴퓨터에 고급 하드웨어가없는 경우 해당 명령을 완료하는 데 SQL Server가 오랜 시간이 걸릴 수 있습니다. 이 작업이 후드 아래에서 어떻게 수행되는지 잘 모르겠지만 내 경험을 바탕으로 데이터베이스에서 레코드를 가져 와서 중복 규칙을 제거하는 트리 구조를 사용하는 프로그램의 메모리를 메모리로 가져와 더 효율적으로 수행 할 수 있습니다. 삽입을 위해. Chuncks (한 번에 10000 행)의 테이블 전체를 ODBC를 사용하여 C ++ 프로그램으로 읽으십시오. C ++ 프로그램 사용 및 STD :: MAP에서 키가 고유 키이고 구조물은 나머지 데이터를 변수로 보유하는 구조물입니다. 모든 레코드를 반복하고 맵에 삽입을 수행하십시오. 맵 삽입 기능은 복제를 제거합니다. 맵 내부의 검색은 LG (N) 시간이기 때문에 While 루프를 사용하는 것보다 복제를 찾는 시간이 훨씬 적습니다. 그런 다음 전체 테이블을 삭제하고 삽입 쿼리를 형성하고 ODBC를 통해 실행하거나 텍스트 파일 스크립트를 작성하고 관리 스튜디오에서 실행하여 맵에서 데이터베이스에 튜플을 추가 할 수 있습니다.

나는 그것이 부정이라고 확신합니다. 그렇지 않으면 거래의 요점은 무엇입니까?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top