2013-01-08 5 views
14

SQL 쿼리가 매우 느리게 실행되고 있습니다. 실행 계획을 살펴본 결과 Files.OrderId가 가장 비용이 많이 드는 작업 (53 %)이라고 주장합니다. OrderId가 어디에서라도 주문하지 않으면 왜 이런 일이 일어날까요? File.OrderId에 색인을 만드는 것이 최선의 방법입니까?내 실행 계획에 정렬이 나타나는 이유는 무엇입니까?

Execution plan 누구든지 관심이 있다면.

with custOrders as 
(
    SELECT c.firstName + ' ' + c.lastname as Customer, c.PartnerId , c.CustomerId,o.OrderId,o.CreateDate, c.IsPrimary 
    FROM Customers c 
    LEFT JOIN CustomerRelationships as cr 
     ON c.CustomerId = cr.PrimaryCustomerId 
    INNER JOIN Orders as o 
     ON c.customerid = o.customerid 
      OR (cr.secondarycustomerid IS NOT NULL AND o.customerid = cr.secondarycustomerid) 
    where c.createdate >= @FromDate + ' 00:00' 
     AND c.createdate <= @ToDate + ' 23:59' 
), 
temp as 
(
SELECT Row_number() 
     OVER ( 
      ORDER BY c.createdate DESC)     AS 'row_number', 
     c.customerid as customerId, 
     c.partnerid as partnerId, 
     c.Customer, 
     c.orderid as OrderId, 
     c.createdate as CreateDate, 
     Count(f.orderid)         AS FileCount, 
     dbo.Getparentcustomerid(c.isprimary, c.customerid) AS ParentCustomerId, 
     au.firstname + ' ' + au.lastname     AS Admin, 
     '' as blank, 
     0 as zero 
FROM custOrders c 
     INNER JOIN files f 
       ON c.orderid = f.orderid 
     INNER JOIN admincustomers ac 
       ON c.customerid = ac.customerid 
     INNER JOIN adminusers au 
       ON ac.adminuserid = au.id 
     INNER JOIN filestatuses s 
       ON f.statusid = s.statusid 
WHERE ac.adminuserid IS NOT NULL 
     AND f.statusid NOT IN (5, 6) 
GROUP BY c.customerid, 
      c.partnerid, 
      c.Customer, 
      c.isprimary, 
      c.orderid, 
      c.createdate, 
      au.firstname, 
      au.lastname 
) 
+0

이 * 오류 동안 소스 XML의 유효성을 검사 * – Kermit

+0

그래, 나는 그것을 보았다 (문제는 키보드와 의자 사이에 존재). 그것이 실제로 변화를 가져 왔습니까? 당신은 여전히 ​​XML을 볼 수 있습니다 ... –

+0

'OVER'는 정렬을 암시하지 않습니까? – Kermit

답변

11

SQL Server에는 두 개의 테이블을 조인해야 할 때 선택할 수있는 알고리즘이 세 가지 있습니다. 중첩 루프 - 조인, 해시 조인 및 정렬 - 병합 - 조인 비용 견적을 기반으로 선택하는 항목 이 경우에는 사용 가능한 정보를 기반으로 Sort-Merge-Join이 올바른 선택이었습니다.

SQL Server 실행 계획에서 Sort-Merge는 데이터가 이미 정렬되어있는 경우와 같이 정렬 작업이 필요하지 않을 수 있으므로 정렬 및 병합 - 결합의 두 가지 연산자로 나뉩니다. 여기 http://sqlity.net/en/1146/a-join-a-day-introduction/ 정렬-Merg이-가입에 대한 기사 : http://sqlity.net/en/1480/a-join-a-day-the-sort-merge-join/


빠르게 쿼리를 만들려면 내가 먼저 인덱스에 보일 것이다 MOR 내용

대해 여기 내 참여 시리즈를 체크 아웃에 가입 . 조회에 클러스터 된 색인 스캔이 있습니다. 당신이 그 중 몇 개를 찾음으로써 당신을 대체 할 수 있다면 당신은 가장 잘 할 것입니다. 또한 SQL Server가 생성하는 예상치가 실제 실행 계획의 실제 행 수와 일치하는지 확인하십시오. SQL Server가 멀리 떨어져있는 경우 SQL Server가 종종 잘못된 선택을합니다. 따라서 더 나은 통계를 제공하면 실적을 쿼리하는 데 도움이 될 수 있습니다.

+0

실제 행 수 및 통계에 대한 +1 –

1

나는 정렬이 조인 발생 생각 : 쿼리도 statusid 열을 사용하기 때문에

FROM custOrders c 
     INNER JOIN files f 
       ON c.orderid = f.orderid 

내가 열 ORDERID 및 statusid 포함 파일에 대한 인덱스를 생성한다.

또한 다음과 같이 변경 고려할 수 있습니다

이이 변경 adminusers 및 admincustomers 사이

  • 가입 내부에 포함되는대로 필요하지 않은

    1. 는 "ac.adminuserid는 NULL을지지 않습니다"는 부정적인 조건은 처리하는 것이 더 비싸기 때문에 "f.statusid NOT IN (5, 6)"을 양수 조건 (예 : In)으로 테스트하십시오.
  • +0

    [files] 이미 덮어 쓰는 인덱스가 있습니다 –

    +0

    그래, 이미'Files'에서 클러스터되지 않은 인덱스 찾기를하고 있습니다. '파일'에서 오는 레코드 수를 줄이려면 어떤 방법이 있습니까? –

    +0

    클러스터되지 않은 인덱스 검색은 성능 측면에서 문제가 없습니다. 레코드 수를 줄이기위한 또 다른 변경 사항은 위에서 추천 한대로 statusid에 대한 NOT IN 체크를 제거하는 것입니다. –

    2

    SQL Server는 정렬 작업을 수행하여 해당 정렬 연산자 오른쪽의 데이터 집합과 Orders 테이블의 레코드 간의 병합 조인을 사용하도록 설정합니다. 병합 조인 자체는 데이터 집합의 모든 레코드를 조인하는 매우 효율적인 방법이지만 조인 할 각 데이터 집합을 조인 키와 순서에 따라 정렬해야합니다.

    키가 이미 OrderID으로 정렬되었으므로 SQL Server는 두 데이터 집합을 병합 할 수 있도록 조인의 다른 쪽 끝 (정렬 오른쪽에있는 다른 항목)을 정렬하여이를 활용하기로 결정했습니다. 계획의 그 시점에서. 병합 병합의 일반적인 대안은 해시 조인이지만 정렬 및 병합 대신 비싼 해시 조인 연산자를 사용하기 때문에 도움이되지 않습니다. 쿼리 최적화 프로그램은이 경우보다 효율적으로 정렬 및 병합을 결정했습니다.

    계획에서 비싼 단계의 근본 원인은 주문 테이블의 모든 레코드를 데이터 집합으로 결합해야하기 때문입니다. 테이블 files에서 오는 레코드를 제한하는 방법이 있습니까? files.statusid에 대한 색인은 5,6에없는 레코드가 전체 테이블 크기의 10 % 미만인 경우 유용 할 수 있습니다.

    QO는 대부분의 레코드가 마지막에 필터링 될 것이라고 생각합니다. 계획의 한가운데서 처리해야하는 레코드가 적어 지도록 해당 필터 조건을 레코드 소스로 다시 밀어 넣으십시오.

    편집 : 나는 우리가 볼 수있는 실행 계획을 갖는 것을 잊어 버렸습니다. 실제 실행 계획 결과를 통해 해당 운영자를 통과하는 실제 레코드 수를 확인할 수있는 방법이 있습니까? 때로는 예상 레코드 수를 약간 벗어날 수 있습니다.

    c.CustomerId=o.CustomerId 
    OR o.CustomerId=cr.SecondaryCustomerId AND cr.SecondaryCustomerId IS NOT NULL 
    

    크로스 이것에 OrdersCustomers까지 사이에 가능한 모든 일치하는 레코드 사이에 조인을 생산하고 SQL 서버처럼 보이는 :

    편집 : 요약 마지막 필터 연산자의 조건 필드 2, 더 깊이 보면 (2 번째에서 마지막 필터 연산자의 오른쪽에있는 계획)을 가리킨 다음 해당 조건이있는 각 레코드를보고 실제로 일치하는지 확인하십시오. 필터에 들어가는 선이 실제로 뚱뚱하고 나오는 선이 실제로 얼마나 얇은 지주의하십시오. 왜냐하면 예상 행 수가 해당 연산자 다음에 21k에서 4로 바뀌기 때문입니다. 앞서 말한 것을 잊어 버리십시오, 이것은 아마도 계획의 주요 문제 일 것입니다. 이러한 열에 인덱스가 있어도 조인 조건이 너무 복잡하기 때문에 SQL Server에서 인덱스를 사용할 수 없습니다. 계획이 즉시 전체 조인 조건부를 사용할 수 없기 때문에 필요한 레코드 만 검색하는 대신 모든 레코드를 병합합니다.

    내 첫 번째 생각은 CTE custOrdersCustomerId을 사용하는 코드와 결합 할 SecondaryCustomerId 코드를 사용하는 두 데이터 세트의 조합으로 바꾸는 것입니다. 이것은 나머지 CTE의 작업을 복제 할 것이지만 인덱스를 적절하게 사용할 수 있다면 큰 이점이 될 수 있습니다.

    +0

    좋아, 실제 계획으로 업데이트했습니다. –

    +0

    원본 예상 요금제와 비슷한 모양입니다. XML 놀이터에서 질문을 편집하거나 문서를 변경 했습니까? –

    0

    나는이 질문이 꽤 오래되었다는 것을 알고있다. 그러나 나는 똑같은 문제를 안고 있었고 나의 테이블이 갑자기 느려졌다는 것을 깨달았다. 증상은 이전과 같이 빠르게 번개를 치고 있던보기를 업데이트하는 속도가 같았습니다. 40 %의 비용을주는 "정렬". 이 솔루션은 누군가에게 유용 할 수 있으며 간단합니다. 테이블을 조인 할 때 "좋아요"와 같은 기준으로 합류하고 있는지 확인하십시오. 신분증에 두 개의 테이블을 가입 시켰습니다. 그러나 한 테이블에서 내 ID는 int로 설정되고 다른 테이블에서는 nvarchar로 설정되었습니다. 나는 이것들을 모두 같은 타입으로 정의하고보기가 번개 속도로 돌아가도록 수정했다.

    희망이 있으면 다른 사람이 일주일에 SQL의 문제점을 파악하려고 노력하는 것을 피할 수 있습니다. 실제로는 PEBKAC 순간입니다.

    관련 문제