2013-05-01 1 views
4

2008 년SQL 서버 성능 및 인덱싱 된 뷰는 SQL 서버를 사용

여러 개의 위치가 각각은 0에서부터 많은 스캔을 가질 수있는 여러 항목을 포함하는 여러 부서를 포함합니다. 각 스캔은 컷오프 시간을 갖거나 갖지 않을 수있는 특정 동작과 관련됩니다. 각 항목은 또한 특정 클라이언트에 속한 특정 프로젝트에 속하는 특정 작업에 속하는 특정 패키지에 속합니다. 각 작업에는 하나 이상의 항목이 들어있는 하나 이상의 패키지가 들어 있습니다.

항목 테이블에는 약 24,000,000 개의 레코드가 있고 스캔 테이블에는 대략 48,000,000 개의 레코드가 있습니다. 새 항목은 하루 동안 데이터베이스에 산발적으로 일괄 적으로 삽입됩니다. 일반적으로 수만 개에 달합니다. 새로운 스캔은 매시간, 수백에서 수십만에 이르는 대량으로 삽입됩니다.

이 테이블들은 어떤 식 으로든 쿼리, 슬라이싱 및 다이 싱됩니다. 나는 매우 구체적인 저장 procs을 쓰고 있었지만 사이트에서 끝이없는 백개의 procs에 직면했을 때 유지 관리의 악몽으로 변했다. (예를 들어, ScansGetDistinctCountByProjectIDByDepartmentIDGroupedByLocationID, ScansGetDistinctCountByPackageIDByDepartmentIDGroupedByLocationID 등과 같은 것). 거의 매일 변화하는 것 같은 느낌이 드는 변화. 칼럼을 변경/추가/삭제해야 할 때마다, 나는 바에서 끝난다.

그래서 필터링 된 그룹화를 결정하기 위해 매개 변수가있는 인덱스 된 뷰와 소수의 일반 저장된 procs를 만들었습니다. 불행히도, 성능은 화장실 아래로 갔다. 첫 번째 질문은 선택 성능이 가장 중요하기 때문에 특정 접근 방식을 고수하고 기본 테이블의 변경을 통해 싸워야한다는 것입니다. 또는 인덱싱 된 뷰/일반 쿼리 방식의 속도를 높이려면 무엇인가 할 수 있습니까? 유지 관리의 악몽을 없애기 위해 실제로 인덱스 된 뷰가 성능을 향상시킬 것으로 기대하고있었습니다.

CREATE VIEW [ItemScans] WITH SCHEMABINDING AS 

SELECT 
    p.ClientID   
    , p.ID  AS [ProjectID]    
    , j.ID  AS [JobID] 
    , pkg.ID  AS [PackageID] 
    , i.ID  AS [ItemID]  
    , s.ID  AS [ScanID] 
    , s.DateTime 
    , o.Code 
    , o.Cutoff 
    , d.ID  AS [DepartmentID] 
    , d.LocationID 
    -- other columns 
FROM 
    [Projects] AS p 
    INNER JOIN [Jobs] AS j 
     ON p.ID = j.ProjectID 
    INNER JOIN [Packages] AS pkg 
     ON j.ID = pkg.JobID 
    INNER JOIN [Items] AS i 
     ON pkg.ID = i.PackageID 
    INNER JOIN [Scans] AS s 
     ON i.ID = s.ItemID 
    INNER JOIN [Operations] AS o 
     ON s.OperationID = o.ID 
    INNER JOIN [Departments] AS d 
     ON i.DepartmentID = d.ID; 

와 클러스터 된 인덱스 :

CREATE UNIQUE CLUSTERED INDEX [IDX_ItemScans] ON [ItemScans] 
(
    [PackageID] ASC, 
    [ItemID] ASC, 
    [ScanID] ASC 
) 

가 여기에 일반적인 저장 발동 중 하나 여기

뷰를 생성하는 코드입니다. 그것은 검사와 차단이 된 항목의 수를 가져옵니다

CREATE NONCLUSTERED INDEX [IX_ItemScans_Counts] ON [ItemScans] 
(
    [Cutoff] ASC 
) 
INCLUDE ([ClientID],[ProjectID],[JobID],[ItemID],[SegmentID],[DepartmentID],[LocationID]) 
: 나는 쿼리를 실행하고 실제 실행 계획을보고 처음으로, 내가 제안하는 누락 된 인덱스를 생성
PROCEDURE [ItemsGetFinalizedCount] 
    @FilterBy  int = NULL 
    , @ID   int = NULL 
    , @FilterBy2 int = NULL 
    , @ID2   sql_variant = NULL 
    , @GroupBy  int = NULL   
WITH RECOMPILE 
AS 
BEGIN 

    SELECT 
     CASE @GroupBy   
      WHEN 1 THEN 
       CONVERT(sql_variant, LocationID) 
      WHEN 2 THEN 
       CONVERT(sql_variant, DepartmentID) 
      -- other cases 
     END AS [ID] 
     , COUNT(DISTINCT ItemID) AS [COUNT] 
    FROM 
     [ItemScans] WITH (NOEXPAND) 
    WHERE  
     (@ID IS NULL OR 
     @ID = CASE @FilterBy    
      WHEN 1 THEN   
       ClientID 
      WHEN 2 THEN 
       ProjectID 
      -- other cases 
     END) 
     AND (@ID2 IS NULL OR 
     @ID2 = CASE @FilterBy2   
      WHEN 1 THEN   
       CONVERT(sql_variant, ClientID) 
      WHEN 2 THEN 
       CONVERT(sql_variant, ProjectID) 
      -- other cases 
     END) 
     AND Cutoff IS NOT NULL 
    GROUP BY 
     CASE @GroupBy   
      WHEN 1 THEN 
       CONVERT(sql_variant, LocationID) 
      WHEN 2 THEN 
       CONVERT(sql_variant, DepartmentID) 
      -- other cases 
     END 
END 

인덱스를 만들면 실행 시간이 약 5 초가 걸렸지 만 이는 여전히 받아 들일 수없는 것입니다 (쿼리의 "특정"버전이 잠시 실행됩니다.) 인덱스에 다른 열을 추가하려고 시도했습니다. 성능 향상 (이 시점에서 내가 무엇을하고 있는지 전혀 알지 못한다.

queryplan

그리고 여기가 컷오프가 NULL이 아닌 경우 뷰의 모든 행을 반환하는 표시 (추구 그 첫 번째 인덱스에 대한 세부 사항입니다) :) 여기

는 쿼리 계획이다

operation

답변

3

일반적인 PROC이 경우 나쁜 생각되지 않을 수도 있습니다,하지만 당신은 당신이 현재하고있는 것처럼 마지막 쿼리에 모든 경우를 넣을 필요가 없습니다.

SQL in the Wild - Catch-all queries

이 방법, 당신은 할 수있어 : 나는 당신의 "특정 쿼리"를 구축하여 일반 시저에서 동적 SQL을 사용하려고 할 것입니다, 거의 같은 방식으로 게일 쇼는 여기에 "캐치 올"쿼리 빌드 블로그 게시판에 나와있는 것처럼 쿼리 계획을 캐시하고 인덱스를 활용할 수 있으며 이후에 수행되는 것과 동일한 초초 성능을 얻을 수 있어야합니다.

+0

빠른 응답을 보내 주셔서 감사합니다. 나는 동적 SQL을 고려하지 않았다는 것을 인정해야한다. 나는 거기에 시간과 장소가 있다는 것을 알고 있지만, 여전히 "동적 인 SQL은 항상 악마 다"라고 내 머리 속에서 울리고 있습니다. 그것을 흔들 수없는 것. – Frank

+1

'sp_executesql' 매개 변수는 procs의 많은 성능을 제공하며,'exec (@sql)'이라는 테러의 대부분을 피할 수 있습니다. 나는 반드시 "사용해야한다 ... PROCS!"의 시대를 생각한다. 불행히도 많은 데이터베이스에서 수 백개의 콜렉션을 유지하고 있지만, 대부분 우리 뒤에 있습니다. –

+2

당신은 이것을 좋아할 것입니다 ... 만약 당신이 저의 저장된 proc을 보았다면, 파라미터를 선언 한 직후에 RECOMPILE을 가지고 있습니다. 그것을 제거하고 저장된 proc의 끝에 OPTION (RECOMPILE)을 추가하면, 그것은 날아간다. 나는 그 차이를 알만큼 충분히 똑똑하지 않습니다.하지만 링크를 게시하게되어 기쁩니다. Erland Sommarskog 사이트에서 저를 알아 차렸습니다. 사람들이 나에게 뭔가를하는 법 대신에 올바른 방향으로 나를 가리킬 때 나는 그것을 더 좋아하는 또 다른 이유. 다시 한번 감사드립니다. – Frank