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


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.