2012-05-22 2 views
0

스토어드 프로 시저에서 SQL Server를 반복하는 것이 좋지 않은 방법에 대한 몇 가지 게시물이 있습니다. 그러나 나는 내가하려고하는 것을 찾지 못했습니다. 우리는 내부적으로 직접 엑셀에 연결할 수있는 데이터 연결을 사용하고 있습니다.SQL Server의 루프에 대한 집합 기반 대안

몇 사람이 대부분의 루프를 표준 쿼리로 변환 할 수 있다고 말한 게시물을 보았습니다. 그러나 나에게있어서 나는이 문제에 곤경을 겪고있다.

유형 38,40의 이벤트 직전에 주문한 모든 custID이 필요합니다. 첫 번째 쿼리에서 이벤트와 주문 사이에 다른 순서가없는 경우에만 가져 오기만하면됩니다.

3 부분으로 구성되어 있습니다. 먼저 임시 테이블에 시간 프레임을 기반으로 모든 주문 (주문 테이블)을 쿼리합니다. 내부는 현재 주문 과거 이전에 시간이 발생할 수있는 고객 이벤트 (LOGEVENT 테이블)을 얻을 수있는 보조 테이블 조인에

Select into temp1 odate, custId from orders where odate>'5/1/12' 

는 다음 나는 임시 테이블을 사용할 수 있습니다. 여기

Select into temp2 eventdate, temp1.custID from LogEvent inner join temp1 on 
temp1.custID=LogEvent.custID where EventType in (38,40) and temp1.odate>eventdate 
order by eventdate desc 

문제는 그 나는 각 고객에 대한 최신 할 첫 번째 쿼리에서 각 고객에 대한 모든 행을 반환합니다 실행하려고하고 쿼리. 그래서 이것은 클라이언트 측에서 모든 이전 이벤트 대신 하나의 이벤트 만 얻도록 반복합니다. 그러나 모든 쿼리가 Excel 내부에서 실행되어야하므로 클라이언트 측을 루프 할 수는 없습니다.

세 번째 단계에서는 두 번째 쿼리의 결과를 사용하여 가장 최근의 주문과 이전 주문간에 이벤트가 발생했는지 확인합니다. 나는 이벤트가 순서를 앞서는 데이터와 그 사이의 다른 명령을 원한다.

Select ordernum, shopcart.custID from shopcart right outer join temp2 on 
shopcart.custID=temp2.custID where shopcart.odate >= temp2.eventdate and 
ordernum is null 

이를 단순화하고 그것이 SQL 서버 대신 내가 클라이언트에서 수행입니다 루프의 일종에서 실행 기반 세트를 만들 수있는 방법이 있나요?

+0

어떤 버전의 sql-server? –

+0

저는 2005 년과 2008 년을 사용하고 있습니다. 2008 년까지 마이그레이션을 시작했지만 끝나지 않았으므로 2005 년에도이를 해결해야합니다. – CaptainBli

+4

5 월 1 일 또는 1 월 5 일입니까? 날짜 리터럴에 대해 안전하고 분명한 형식을 사용하십시오. ' '20120501 '' ... SQL Server는이 정보를 오해하지 않으며 사용자, 동료 또는 독자도 오해하지 않습니다. –

답변

1

이것은 설정 기반 표기법으로 바꾸는 좋은 예입니다.

먼저 세 가지 쿼리를 모두 하나의 쿼리로 결합했습니다. 일반적으로 단일 쿼리를 사용하면 쿼리 최적화 프로그램이 최선을 다해 실행 경로를 결정합니다. 또한 다중 스레드/다중 프로세서 시스템에서 우발적 인 u 리 직렬화를 f 지합니다.

가장 최근의 값이 1이되도록 이벤트를 순서화하기위한 키는 row_number()입니다. 마지막 WHERE 절에서이를 볼 수 있습니다.

select ordernum, shopcart.custID 
from (Select eventdate, temp1.custID, 
      row_number() over (partition by temp1.CustID order by EventDate desc) as seqnum 
     from LogEvent inner join 
      (Select odate, custId 
      from order 
      where odate>'5/1/12' 
      ) temp1 
      on temp1.custID=LogEvent.custID 
     where EventType in (38,40) and temp1.odate>eventdate order by eventdate desc 
    ) temp2 left outer join 
    ShopCart 
    on shopcart.custID=temp2.custID 
where seqnum = 1 and shopcart.odate >= temp2.eventdate and ordernum is null 

"주문에서"구문 오류가 발생해야한다고 생각하지만 귀하의 이름 지정 규칙을 지켰습니다. 그렇지 않은 경우에도 예약 된 SQL 단어로 테이블과 컬럼의 이름을 지정하는 것은 나쁜 습관입니다.

+0

예. 주문하지 않아야합니다. – CaptainBli

+0

row_number()는 주어진 고객에 대한 모든 이벤트를 반복합니다. 고객별로 하나의 이벤트 행을 반복하고 요청하는 것보다 빠릅니다. 조회 된 고객 그룹에 대해 10000 개의 이벤트 행이있을 수 있습니다. 나는 모든 것을 이해하려고 노력하고 있습니다. – CaptainBli

+1

예! 데이터베이스 내부의 "루핑"은 커서를 사용하여 루핑하는 것보다 훨씬 빠릅니다. 커서는 오버 헤드가 많아 데이터베이스에서 앞뒤로 각 값을 전달합니다. 커서는 순차적으로 실행되고, 데이터베이스는 병렬로 실행됩니다. –

0

새 버전의 SQL Server를 사용하는 경우 ROW_NUMBER 기능을 사용할 수 있습니다. 나는 곧 예제를 쓸 것이다.

;WITH myCTE AS 
( 
SELECT 
    eventdate, temp1.custID, 
    ROW_NUMBER() OVER (PARTITION BY temp1.custID ORDER BY eventdate desc) AS CustomerRanking 
FROM LogEvent 
JOIN temp1 
    ON temp1.custID=LogEvent.custID 
WHERE EventType IN (38,40) AND temp1.odate>eventdate 
) 
SELECT * into temp2 from myCTE WHERE CustomerRanking = 1; 

이것은 루프없이 각 고객의 최신 이벤트를 가져옵니다.

또한 RANK을 사용할 수 있지만 ROW_NUMBER는 파티션에 대해 중복 된 번호를 보장하지 않지만 연관성을 위해 중복을 생성합니다.

+0

나는이 아이디어가 마음에 들지만 초기 쿼리에서 모든 고객에 대해 전체 이벤트 테이블을 반복해야 할 것입니다. 이것은 수만 건의 레코드가 될 가능성이 있습니다. 루프가 각 주문에 대해 단일 레코드를 제공 할 수있는 위치. – CaptainBli

+1

@CaptainBli 절차 논리가 아닌 집합 기반 논리로 생각해야합니다. 이렇게하면 검색어가 집합으로 제한됩니다. CTE를 사용하여 첫 번째 쿼리에서 = 1 인 고객 순위를 포함하도록 내 대답을 수정했습니다. 옵티마이 저는 나머지를 처리해야합니다. 불필요한 루프는 SQL에서 항상 나쁜 생각입니다. 이미 언급했듯이 :) –

+1

@CaptainBli : 쿼리 계획을보기 전까지는이 쿼리가 비효율적인지 알 수 없습니다. 지원 인덱스를 사용하면 쿼리는 Segment 및 Top 연산자를 사용하여'CustomerEvent = 1' 절로 인해'LogEvent'의 모든 행을 처리하지 않도록 할 수 있습니다. (내 의견에서 귀하의 질문에 관계없이 이것에 관계없이, 나는 쿼리가 잘못되었다고 생각합니다. 여기 내 의견은 "전체 이벤트 테이블을 반복해야 할 것입니다."라는 귀하의 의견에만 반응합니다.) –