분리 된 대형 세트를 효율적으로 필터링


9

하나의 테이블이 있다고 가정 해 봅시다.

CREATE TABLE Ticket (
    TicketId int NOT NULL,
    InsertDateTime datetime NOT NULL,
    SiteId int NOT NULL,
    StatusId tinyint NOT NULL,
    AssignedId int NULL,
    ReportedById int NOT NULL,
    CategoryId int NULL
);

이 예 TicketId에서는 기본 키입니다.

사용자가이 테이블에 대해 "부분적으로 임시"쿼리를 만들 수 있기를 바랍니다. 쿼리의 일부가 항상 고정되기 때문에 부분적으로 말합니다.

  1. 쿼리는 것입니다 항상 온 다양한 필터를 수행 InsertDateTime
  2. 쿼리는 항상 ORDER BY InsertDateTime DESC
  3. 쿼리 결과가 페이지에 표시됩니다

사용자는 다른 열을 선택적으로 필터링 할 수 있습니다. 그들은 하나, 또는 여러 개를 필터링 할 수 있습니다. 그리고 각 열에 대해 사용자는 분리로 적용될 일련의 값 중에서 선택할 수 있습니다. 예를 들면 다음과 같습니다.

SELECT
    TicketId
FROM (
    SELECT
        TicketId,
        ROW_NUMBER() OVER(ORDER BY InsertDateTime DESC) as RowNum
    FROM Ticket
    WHERE InsertDateTime >= '2013-01-01' AND InsertDateTime < '2013-02-01'
      AND StatusId IN (1,2,3)
      AND (CategoryId IN (10,11) OR CategoryId IS NULL)
    ) _
WHERE RowNum BETWEEN 1 AND 100;

이제 테이블에 100,000,000 개의 행이 있다고 가정하십시오.

내가 얻을 수있는 최선의 방법은 각각의 "선택적"열을 포함하는 포함 인덱스입니다.

CREATE NONCLUSTERED INDEX IX_Ticket_Covering ON Ticket (
    InsertDateTime DESC
) INCLUDE (
    SiteId, StatusId, AssignedId, ReportedById, CategoryId
);

이것은 다음과 같은 쿼리 계획을 제공합니다.

  • 고르다
    • 필터
      • 상단
        • 시퀀스 프로젝트 (컴퓨 트 스칼라)
          • 분절
            • 색인 찾기

꽤 좋아 보인다. 비용의 약 80 % -90 %는 Index Seek 작업에서 발생하며 이는 이상적입니다.

이런 종류의 검색을 구현하기위한 더 나은 전략이 있습니까?

경우에 따라 "고정 된"부분의 결과 세트가 100 또는 1000 일 수 있기 때문에 선택적 필터링을 클라이언트에 오프로드하지 않아도됩니다. 그런 다음 클라이언트는 정렬 및 페이징을 담당하여 클라이언트에게 너무 많은 작업을 수행 할 수도 있습니다.


하위 쿼리를 임시 테이블 또는 테이블 변수에 배치하고 그렇게 만들 수 있습니까? 더 큰 테이블을 사용하면 하위 쿼리에 걸리는 경우가 있습니다. 커버링 인덱스는 지금까지만 가능합니다.
Valkyrie 2013

@Valkyrie는 엄청나게 비효율적 인 것 같습니다. 또한이 쿼리의 변형 (다른 매개 변수 및 다른 선택적 where 절)은 하루 종일 하루에 몇 번 실행되며 평균 100ms 미만의 결과를 반환해야한다는 점을 고려하십시오. 우리는 이미이 작업을 수행하고 있으며 현재로서는 제대로 작동합니다. 확장성에 대한 성능을 계속 향상시키는 방법에 대한 아이디어를 찾고 있습니다.
Joseph Daigle

저장 공간 사용에 얼마나 관심이 있습니까?
Jon Seigel

@ JonSeigel 그것은 얼마나 많은에 달려 있지만 ... 나는 어떤 제안을보고 싶습니다
Joseph Daigle

2
그리고 결과의 두 번째 페이지를 얻는 방법 / 쿼리는 무엇입니까? RowNum BETWEEN 101 AND 200?
ypercubeᵀᴹ

답변:


1

이 특정 작업로드가 테이블에 대한 대부분의 쿼리 인 경우 다음을 고려할 수 있습니다.

ALTER TABLE Ticket ADD CONSTRAINT PK_Ticket PRIMARY KEY NONCLUSTERED (TicketId);

CREATE UNIQUE CLUSTERED INDEX IX_Ticket_Covering ON Ticket (
    InsertDateTime ASC
);

고려 사항 :

  • datetime2를 사용할 수 있습니까 (SQL 2008+; 유연한 정밀도)
  • InsertDateTime은 정밀도 내에서 고유합니다.
  • 시간이 제한되지 않으면 고유 SQL은 int 유형의 숨겨진 고유 열을 추가합니다. 클러스터되지 않은 모든 인덱스에 추가되어 올바른 클러스터 된 레코드를 참조 할 수 있습니다.

장점 :

  • 테이블 끝에 새 행을 추가합니다
  • 선택적 필터 열을 두 번 작성하지 마십시오 (클러스터에서 한 번, 포함하기 위해 인덱스 리프에서 한 번)
  • 대부분의 시간은 더 많거나 적은 파일러로 클러스터 인덱스 검색을 계속합니다.
  • 그런 다음 가장 인기있는 열 쌍에 다른 비 클러스터형 인덱스를 추가하십시오.

1

과거에는이 기술을 사용했습니다. 테이블은 크지 않았지만 검색 기준은 더 복잡했습니다.

이것은 짧은 버전입니다.

CREATE PROC usp_Search
    (
    @StartDate  Date,
    @EndDate    Date,
    @Sites      Varchar(30) = NULL,
    @Assigned   Int = NULL, --Assuming only value possible
    @StartRow   Int,
    @EndRow     Int
    )
AS
DECLARE @TblSites   TABLE (ID Int)
IF @Sites IS NOT NULL
BEGIN
    -- Split @Sites into table @TblSites
END
SELECT  TicketId
FROM    (
        SELECT  TicketId,
                ROW_NUMBER() OVER(ORDER BY InsertDateTime DESC) as RowNum
        FROM    Ticket
                LEFT JOIN @TblSites
                    Ticket.SiteID = @TblSites.ID
        WHERE   InsertDateTime >= @StartDate 
                AND InsertDateTime < @EndDate
                AND (
                    @Assigned IS NULL 
                    OR AssignedId = @Assigned 
                    )
        ) _
WHERE   RowNum BETWEEN @StartRow AND @EndRow;

1

처음 두 가지 전제 조건을 감안할 때에 클러스터 인덱스를 살펴볼 것입니다 InsertDateTime.



-1

클라이언트가 거의 같은 방식으로 반복해서 필터링하는 경우 해당 쿼리에 대한 인덱스를 만들 수 있습니다.

예를 들어 클라이언트가 SiteId 및 StatusId를 필터링하는 경우 추가 색인을 작성할 수 있습니다.

CREATE NONCLUSTERED INDEX IX_Ticket_InsertDateTime_SiteId_StatusId ON Ticket     
(InsertDateTime DESC,
 SiteId [ASC/DESC],
 StatusId [ASC/DESC] ) 
 INCLUDE ( ... );

이렇게하면 대부분의 '보다 일반적인'쿼리가 빠르게 실행될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.