2014-06-17 4 views
4

다른 공급 업체에서 생성 한 CSV 파일과 300 개 이상의 구조적으로 동일한 CRM 데이터베이스간에 데이터 동기화 프로세스를 만드는 작업이 있습니다. 모든 CRM 데이터베이스는 동일한 SQL Server 인스턴스에 정의됩니다. 세부 사항은 다음과 같습니다.여러 조회에 권장되는 프로그래밍 패턴

원본 데이터는 고객이 마케팅 커뮤니케이션을 선택한 모든 이메일 주소의 목록을 포함하는 CSV입니다. 이 CSV 파일은 매일 밤 전체적으로 전송되지만 마지막 처리주기 이후에 수정 된 레코드 만 선택할 수있는 레코드 수준 날짜/시간 스탬프가 포함됩니다. CSV 파일에는 잠재적으로 수십만 개의 행이 있지만 매일 변경되는 예상 변경 사항은 그보다 훨씬 적습니다.

CSV에서 데이터를 선택하고 각 행을 맞춤 List<T> 개체로 변환합니다.

일단 CSV가 쿼리되고 데이터가 변형되면이 List<T>의 내용을 CRM 데이터베이스와 비교해야합니다. 이것은 특정 이메일 주소가 CSV 파일에 포함되어 있다는 사실에 기인 할 수있다 :

  • 여러에 존재하는
  • 는 300 데이터베이스 중 하나에 존재하는 300 데이터베이스의 존재하지 않음 데이터베이스

마스터 CSV 목록의 전자 메일 주소와 CRM 데이터베이스가 일치하는 경우 일치하는 CRM 레코드가 CSV 파일에 포함 된 값으로 업데이트됩니다.

foreach(string dbName in masterDatabaseList) 
{ 
    //open db connection 

    foreach(string emailAddress in masterEmailList) 
    { 
     //some helper method that would execute a SQL statement like 
     //"IF EXISTS ... WHERE EMAIL_ADDRESS = <emailAddress>" return true; 

     bool matchFound = EmailExistsInDb(emailAddress) 

     if (matchFound) 
     { 
      //the current email from the master list does exist in this database 
      //do necessary updates and stuff 
     } 
    } 
} 

이 가장 효율적인 방법입니다 : 높은, 매우 일반적인 수준에서

가, 나는 이런 식으로 뭔가를해야 할 것이라고 생각했다? 마스터 CSV 목록에있는 각 이메일이 존재하는지 확인하기 위해 300 개의 데이터베이스를 잠재적으로 수천 번 방문해야하는 것을 원하지 않습니다. 이상적으로, 나는의 라인을 따라 SQL 문 생성하고 싶습니다 : 이것은 단일 쿼리를 허용 할

"SELECT * FROM EMAIL_TABLE WHERE EMAIL_ADDRESS IN(email1,email2, email3,...)" 

가 데이터베이스에 대해 실행되는, 그러나 나는이 방법이 더 좋은 지 여부를 모른다/더 효율적입니다. 특히 SQL을 동적으로 생성해야하고 잠재적으로이를 사출까지 열 수 있기 때문입니다.

이 시나리오에서 가장 좋은 방법은 무엇입니까? 매번 비교해야 할 300 개의 데이터베이스가 있으므로 최소한의 처리 시간으로 최상의 결과를 얻을 수있는 방법을 찾고 있습니다. 프로덕션 코드에서는 여러 데이터베이스를 동시에 처리 할 수 ​​있도록 다중 스레드 접근 방식을 구현하므로 모든 접근 방식은 스레드로부터 안전해야합니다.

답변

1

기본 아이디어가 옳은 것 같습니다. CSV에있는 모든 회선에 대해 데이터베이스를 한 번 치는 것은 너무 느립니다. 그러나

var addresses = GetEmailAddresses(); 
var entries = ctx.Entries.Where(e => addresses.Contains(e.EmailAddress)); 

당신이 목록에 너무 많은 주소가 있다면, 그것은 생성하고 쿼리를 평가하는 데 오랜, 오랜 시간이 걸릴 것입니다 : 당신은 LINQ를 통해 문 "여기서의"과 같이 만들 수 있습니다. 합리적인 크기 (200 개 항목)의 일괄 처리로 입력 목록을 분할 한 다음 위의 트릭을 사용하여 단일 데이터베이스 검사로 각 일괄 처리를 처리하는 것이 좋습니다.

당신이 작업을했으면, 당신은 그들이 성능 현명한 측정 가능한 차이를 만드는 있는지 확인하기 위해 몇 가지 다른 일을 시도 할 수 있습니다 :

  1. 가 배치 크기를 조정할합니다.
  2. 다양한 병렬 처리 수준으로 개별적으로 배치를 실행하십시오.
  3. 데이터베이스 테이블, 특히 전자 메일 주소 필드의 인덱스로 재생하십시오.
  4. 일괄 처리하기 전에 이메일 주소를 주문하십시오. db 쿼리가 하드 디스크 캐싱 전략을보다 잘 활용할 수 있습니다.
+2

당신은 또한'async'와'닷넷 프레임 워크의 최신 버전에서 발견 await' 기능을 활용할 수 있습니다. 한 번에 여러 Dbs를 (비동기 적으로) 공격 할 수는 있지만, 한 번에 300을 시도하려고하는 것은 아닙니다. – ps2goat

+1

답장을 보내 주셔서 감사합니다. 앞으로 살펴볼 몇 가지 접근법이 분명히 있습니다. 물론이 질문을 게시하자마자 클라이언트가 돌아와서 요구 사항을 변경했기 때문에이 복잡한 조회 프로세스가 더 이상 필요하지 않습니다. 피규어. :) –

0

csv 목록 개체의 내용을 표 값 매개 변수에 넣을 수 있습니다. 그런 다음 해당 TVP를 전달하는 저장 프로 시저를 호출합니다. 스토어드 프로 시저는 300 개의 데이터베이스를 통해 커서를 실행하고 테이블 값 매개 변수에 조인 할 수 있습니다 (ad-hoc sql 사용). 그것은 기본적으로 너무 나쁘지 않은 300 번을 반복하는 루프가 될 것입니다.이 같은 뭔가 는 :

CREATE PROCEDURE yourNewProcedure 
(
    @TableValueParameter dbo.udtTVP READONLY 
) 
AS 

DECLARE @dbName varchar(255) 
DECLARE @SQL nvarchar(3000) 

DECLARE DB_Cursor CURSOR LOCAL FOR 
    SELECT DISTINCT name 
    FROM sys.databases 
    WHERE Name like '%yourdbs%' 
OPEN DB_Cursor 
FETCH NEXT FROM DB_Cursor INTO @dbName 
WHILE @@FETCH_STATUS = 0 
BEGIN 
    SET @SQL = 'UPDATE t 
       SET t2.Field = t.Field    
       FROM @TableValueParameter t 
       JOIN [' + @dbName + ']..TableYouCareAbout t2 ON t.Field = t2.Field ' 

    EXEC sp_executesql @SQL, N'@TableValueParameter dbo.udtTVP', @TableValueParamete 

    FETCH NEXT FROM DB_Cursor INTO @dbName 
END 
CLOSE DB_Cursor 
DEALLOCATE DB_Cursor 
+0

이것은 정말 흥미로운 접근 방법이며 실제로 고려하지 않은 방법입니다. 필자는 디버깅과 오류 처리 및 로깅 등을보다 효율적으로 제어 할 수 있기 때문에 항상 코드에서 이와 같은 작업을 수행하기를 선호했습니다. 그러나 앞으로는 정상적으로 수행 할 때와 비교하여 조사 할 가치가 있습니다. –