GROUP BY 절을 사용하지 않고 집계 쿼리를 사용하는 것보다 집계 쿼리가 훨씬 빠른 이유는 무엇입니까?


12

왜 집계 쿼리가 GROUP BY없는 경우보다 절을 사용하여 쿼리가 훨씬 빨리 실행되는지 궁금 합니다.

예를 들어이 쿼리를 실행하는 데 거의 10 초가 걸립니다.

SELECT MIN(CreatedDate)
FROM MyTable
WHERE SomeIndexedValue = 1

이건 1 초도 걸리지 않지만

SELECT MIN(CreatedDate)
FROM MyTable
WHERE SomeIndexedValue = 1
GROUP BY CreatedDate

CreatedDate이 경우에는 하나만 있으므로 그룹화 된 쿼리는 그룹화되지 않은 것과 동일한 결과를 반환합니다.

두 쿼리의 실행 계획이 다르다는 것을 알았습니다. 두 번째 쿼리는 Parallelism을 사용하지만 첫 번째 쿼리는 그렇지 않습니다.

Query1 실행 계획 Query2 실행 계획

GROUP BY 절이 없으면 SQL Server가 집계 쿼리를 다르게 평가하는 것이 정상입니까? 그리고 GROUP BY절 을 사용하지 않고 첫 번째 쿼리의 성능을 향상시키기 위해 할 수있는 일이 있습니까?

편집하다

방금 OPTION(querytraceon 8649)병렬 처리의 비용 오버 헤드를 0으로 설정하는 데 사용할 수 있다는 것을 배웠습니다. 이 쿼리를 사용하면 병렬 처리를 사용하고 런타임을 2 초로 단축하지만이 쿼리 힌트를 사용하는 데 단점이 있는지는 알지 못합니다.

SELECT MIN(CreatedDate)
FROM MyTable
WHERE SomeIndexedValue = 1
OPTION(querytraceon 8649)

여기에 이미지 설명을 입력하십시오

쿼리는 사용자 선택시 값을 채우는 것이기 때문에 런타임이 더 짧기를 선호하므로 그룹화 된 쿼리와 같이 순간적으로 이상적이어야합니다. 지금은 쿼리를 래핑하고 있지만 실제로 이상적인 솔루션은 아닙니다.

SELECT Min(CreatedDate)
FROM
(
    SELECT Min(CreatedDate) as CreatedDate
    FROM MyTable WITH (NOLOCK) 
    WHERE SomeIndexedValue = 1
    GROUP BY CreatedDate
) as T

편집 # 2

Martin의 추가 정보 요청에 대한 답변 :

모두 CreatedDateSomeIndexedValue그들에 별도의 고유하지 않은 비 클러스터형 인덱스가 있습니다. SomeIndexedValue다른 테이블의 PK (int)를 가리키는 숫자 값을 저장하더라도 실제로는 varchar (7) 필드입니다. 두 테이블 간의 관계는 데이터베이스에 정의되어 있지 않습니다. 데이터베이스를 전혀 변경하지 않아야하며 데이터를 쿼리하는 쿼리 만 쓸 수 있습니다.

MyTable는 3 백만 개가 넘는 레코드를 포함하며 각 레코드에는 자신이 속한 그룹이 할당됩니다 ( SomeIndexedValue). 그룹은 1 ~ 200,000 개의 레코드가 될 수 있습니다.

답변:


8

아마도 CreatedDate가장 낮은 것부터 높은 것까지 순서대로 인덱스를 따르고 SomeIndexedValue = 1술어 를 평가하기 위해 조회를 수행하는 것 같습니다 .

첫 번째 일치하는 행을 찾으면 수행되지만 행을 찾기 전에 예상 한 것보다 더 많은 조회를 수행하는 것이 좋습니다 (조건 자와 일치하는 행이 날짜에 따라 무작위로 분산 된 것으로 가정).

비슷한 문제에 대한 내 답변보기

이 쿼리에 대한 이상적인 인덱스는에 있습니다 SomeIndexedValue, CreatedDate. 당신이 그를 추가하거나 적어도 기존 인덱스를 만들 수 있다고 가정 SomeIndexedValue커버 CreatedDate는 포함 된 열로 것은 당신은 다음과 같이 쿼리를 다시 작성을 시도 할 수

SELECT MIN(DATEADD(DAY, 0, CreatedDate)) AS CreatedDate
FROM MyTable
WHERE SomeIndexedValue = 1

특정 계획을 사용하지 못하게합니다.


2

MAXDOP를 제어하고 알려진 테이블 (예 : AdventureWorks.Production.TransactionHistory)을 선택할 수 있습니까?

사용하여 설정을 반복하면

--#1
SELECT MIN(TransactionDate) 
FROM AdventureWorks.Production.TransactionHistory
WHERE TransactionID = 100001 
OPTION( MAXDOP 1) ;

--#2
SELECT MIN(TransactionDate) 
FROM AdventureWorks.Production.TransactionHistory
WHERE TransactionID = 100001 
GROUP BY TransactionDate
OPTION( MAXDOP 1) ;
GO 

비용은 동일합니다.

제쳐두고, 나는 인덱스 값에 대한 인덱스 탐색을 기대할 것입니다. 그렇지 않으면 스트림 집계 대신 해시 일치가 표시 될 수 있습니다. 집계하는 값이 포함 된 비 클러스터형 인덱스로 성능을 향상 시키거나 집계를 열로 정의하는 인덱스 된 뷰를 만들 수 있습니다. 그런 다음 인덱싱 된 ID에 의해 집계가 포함 된 클러스터형 인덱스에 도달합니다. SQL Standard에서는 뷰를 생성하고 WITH (NOEXPAND) 힌트를 사용할 수 있습니다.

예 (인덱싱 된 뷰에서 작동하지 않기 때문에 MIN을 사용하지 않습니다) :

USE AdventureWorks ;
GO

-- Covering Index with Include
CREATE INDEX IX_CoverAndInclude
ON Production.TransactionHistory(TransactionDate) 
INCLUDE (Quantity) ;
GO

-- Indexed View
CREATE VIEW dbo.SumofQtyByTransDate
    WITH SCHEMABINDING
AS
SELECT 
      TransactionDate 
    , COUNT_BIG(*) AS NumberOfTransactions
    , SUM(Quantity) AS TotalTransactions
FROM Production.TransactionHistory
GROUP BY TransactionDate ;
GO

CREATE UNIQUE CLUSTERED INDEX SumofAllChargesIndex 
    ON dbo.SumofQtyByTransDate (TransactionDate) ;  
GO


--#1
SELECT SUM(Quantity) 
FROM AdventureWorks.Production.TransactionHistory 
WITH (INDEX(0))
WHERE TransactionID = 100001 
OPTION( MAXDOP 1) ;

--#2
SELECT SUM(Quantity)  
FROM AdventureWorks.Production.TransactionHistory 
WITH (INDEX(IX_CoverAndInclude))
WHERE TransactionID = 100001 
GROUP BY TransactionDate
OPTION( MAXDOP 1) ;
GO 

--#3
SELECT SUM(Quantity)  
FROM AdventureWorks.Production.TransactionHistory
WHERE TransactionID = 100001 
GROUP BY TransactionDate
OPTION( MAXDOP 1) ;
GO

MAXDOP최대 병렬 처리 수준을 설정하여 쿼리에서 사용할 수있는 프로세서 수를 제한합니다. 기본적으로 두 번째 쿼리가 첫 번째 쿼리보다 느리게 실행됩니다. 병렬 처리 기능을 제거하고 있기 때문에 원하는 것이 아닙니다.
Rachel

@Rachel 동의합니다. 그러나 기본 규칙을 설정하지 않으면 아무것도 비교할 수 없습니다. 64 코어에서 실행되는 병렬 프로세스를 하나의 스레드에서 실행하는 단일 프로세스와 쉽게 비교할 수 없습니다. ) - 결국, 나는 모든 우리의 기계는 적어도 하나 개의 논리 CPU =이 희망
ooutwire

0

내 의견으로는 문제의 이유는 SQL 서버 최적화 프로그램이 BEST 계획을 찾지 않고 오히려 병렬 처리를 강요 한 후에 쿼리가 훨씬 빨리 실행되었다는 사실에서 알 수 있듯이 최적 계획을 찾고 있기 때문입니다. 자체적으로 수행되지 않습니다.

또한 쿼리를 다른 형식으로 다시 작성하는 것이 병렬화의 차이점이라는 많은 상황을 보았습니다 (예를 들어 SQL의 대부분의 기사는 매개 변수화를 권장하지만 스니핑 된 매개 변수가 아닌 것과 동일하더라도 때로는 병렬화되는 원인이 있음을 발견했습니다) -병렬화 된 하나 또는 두 개의 쿼리를 UNION ALL과 결합하면 때로는 병렬화를 제거 할 수 있습니다).

따라서 올바른 해결책은 임시 테이블, 테이블 변수, cte, 파생 테이블, 매개 변수화 등과 같은 쿼리 작성 방법을 시도하고 인덱스, 인덱싱 된 뷰 또는 필터링 된 인덱스를 사용하는 것입니다. 최고의 계획을 얻으려면.

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