Toplam sorgu neden GROUP BY deyimiyle bir tane olmadan önemli ölçüde daha hızlı?


12

Neden bir toplu sorgunun bir GROUP BYcümle ile neden birden çok daha hızlı çalıştığını merak ediyorum .

Örneğin, bu sorgunun çalışması yaklaşık 10 saniye sürer

SELECT MIN(CreatedDate)
FROM MyTable
WHERE SomeIndexedValue = 1

Bu bir saniyeden az sürerken

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

CreatedDateBu durumda yalnızca bir tane vardır, bu nedenle gruplandırılmış sorgu, grubu çözülmüş olanla aynı sonuçları döndürür.

İki sorgu için yürütme planları farklı olduğunu fark ettim - İkinci sorgu ilk sorgu değil iken Parallelism kullanır.

Sorgu1 Yürütme Planı Sorgu2 Yürütme Planı

SQL Server için bir GROUP BY yan tümcesi yoksa bir toplu sorgu farklı değerlendirmek normal mi? Ve bir GROUP BYcümle kullanmadan 1. sorgunun performansını artırmak için yapabileceğim bir şey var mı?

Düzenle

Ben sadece OPTION(querytraceon 8649)bu sorgu ipucu kullanmak için herhangi bir dezavantajı olup olmadığını bilmiyorum rağmen, sorgu bazı paralellik kullanmak yapar ve çalışma süresini 2 saniye azaltır yapar, 0 paralellik maliyet yükü ayarlamak için kullanabilirsiniz öğrendim .

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

resim açıklamasını buraya girin

Sorgu kullanıcı seçimi üzerine bir değer doldurmak için amaçlandığı için hala daha kısa bir çalışma zamanı tercih ediyorum, bu yüzden ideal bir şekilde gruplandırılmış sorgu gibi anlık olmalıdır. Şu anda sadece sorgumu sarım ama bunun gerçekten ideal bir çözüm olmadığını biliyorum.

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

Düzenle # 2

Martin'in daha fazla bilgi talebine yanıt olarak :

Hem CreatedDateve SomeIndexedValueonlara ayrı bir benzersiz olmayan, sigara kümelenmiş bir dizin var. SomeIndexedValueaslında başka bir tablonun PK (int) değerini gösteren sayısal bir değer saklasa da bir varchar (7) alanıdır. İki tablo arasındaki ilişki veritabanında tanımlanmamıştır. Ben hiç veritabanını değiştirmek gerekiyordu ve sadece veri sorgulayan sorgular yazabilirsiniz.

MyTable3 milyondan fazla kayıt içerir ve her kayda ait olduğu bir grup atanır ( SomeIndexedValue). Gruplar 1 ile 200.000 arasında kayıt olabilir

Yanıtlar:


8

Görünüşe göre CreatedDateen düşükten en yükseğe doğru bir indeksi takip ediyor ve SomeIndexedValue = 1yüklemi değerlendirmek için arama yapıyor .

İlk eşleşen satırı bulduğunda yapılır, ancak böyle bir satır bulmadan önce beklediğinden çok daha fazla arama yapıyor olabilir (yüklemle eşleşen satırların tarihe göre rastgele dağıtıldığını varsayar.)

Benzer bir sorun için cevabımı burada görebilirsiniz

Bu sorgu için ideal dizin bir olacaktır SomeIndexedValue, CreatedDate. Bunu ekleyemeyeceğinizi veya en azından mevcut dizini SomeIndexedValuekapakta CreatedDatedahil edilen bir sütun olarak oluşturamayacağınızı varsayarak sorguyu aşağıdaki gibi yeniden yazmayı deneyebilirsiniz

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

belirli bir planı kullanmasını önlemek için.


2

MAXDOP'u kontrol edebilir ve bilinen bir tabloyu seçebilir miyiz, örn., AdventureWorks.Production.TransactionHistory?

Kurulumunuzu kullanarak tekrarladığımda

--#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 

maliyetler aynı.

Bir kenara, ben endeksli değeri üzerinde bir dizin arayışı (olmasını sağlamak) beklenir; Aksi takdirde, büyük olasılıkla akış toplamaları yerine karma eşleşmeleri göreceksiniz. Topladığınız değerleri içeren kümelenmemiş dizinlerle performansı artırabilir veya toplamlarınızı sütun olarak tanımlayan dizinlenmiş bir görünüm oluşturabilirsiniz. Daha sonra, birleştirilmiş Kimliğe göre toplamalarınızı içeren kümelenmiş bir dizine isabet edersiniz. SQL Standard'da, görünümü oluşturabilir ve WITH (NOEXPAND) ipucunu kullanabilirsiniz.

Bir örnek (dizinli görünümlerde çalışmadığından MIN kullanmıyorum):

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

MAXDOPsorgunun kullanabileceği işlemci sayısını sınırlayan maksimum paralellik derecesini ayarlar. Bu temelde 2. sorguyu birincisi kadar yavaş çalıştıracaktır, çünkü parazit kullanma yeteneklerini kaldırıyor, ki bu benim istediğim şey değil.
Rachel

@Rachel katılıyorum; ancak bazı temel kurallar koymadıkça hiçbir şeyi karşılaştıramayız. 64 çekirdekteki paralel bir işlemi tek bir iş parçacığında karşılaştıramıyorum. Sonunda, umarım tüm makinelerimiz en az bir mantıksal CPU'ya sahiptir = -)
ooutwire 20:12

0

Bence sorunun nedeni, sql server optimizer'ın EN İYİ planı aramaması, paralelliği zorladıktan sonra sorgunun çok daha hızlı yürütüldüğünden, optimize edicinin sahip olduğu bir şeyden de anlaşılacağı gibi, iyi bir plan aramasıdır. kendi başına yapılmaz.

Ayrıca farklı bir biçimde sorguyu yeniden yazma arasındaki birçok durum gördüm (örneğin SQL çoğu makalede parametreleştirme tavsiye rağmen her ne kadar bile sniffed parametreleri bir olmayan aynı olsa bile noy paralel hale neden bulduk - paralel hale getirilmiş veya iki sorguyu UNION ALL ile birleştirmek bazen paralelleştirmeyi ortadan kaldırabilir).

Bu nedenle doğru çözüm, sorguyu yazmanın, geçici tabloları, tablo değişkenlerini, cte, türetilmiş tabloları, parametreleştirmeyi vb. Denemek ve ayrıca dizinlerle, dizinlenmiş görünümler veya filtrelenmiş dizinlerle oynamak gibi farklı yollar deneyerek olabilir. en iyi planı almak için.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.