it-swarm.asia

Mengapa kueri agregat secara signifikan lebih cepat dengan klausa GROUP BY daripada tanpa klausa?

Saya hanya ingin tahu mengapa kueri agregat berjalan jauh lebih cepat dengan klausa GROUP BY Daripada tanpa klausa.

Misalnya, kueri ini membutuhkan waktu hampir 10 detik untuk dijalankan

SELECT MIN(CreatedDate)
FROM MyTable
WHERE SomeIndexedValue = 1

Sementara yang ini membutuhkan waktu kurang dari satu detik

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

Hanya ada satu CreatedDate dalam kasus ini, jadi kueri yang dikelompokkan mengembalikan hasil yang sama dengan yang tidak dikelompokkan.

Saya perhatikan rencana eksekusi untuk dua kueri berbeda - Kueri kedua menggunakan Paralelisme sedangkan kueri pertama tidak.

Query1 Execution PlanQuery2 Execution Plan

Apakah normal untuk server SQL untuk mengevaluasi permintaan agregat berbeda jika tidak memiliki klausa GROUP BY? Dan adakah yang bisa saya lakukan untuk meningkatkan kinerja kueri 1 tanpa menggunakan klausa GROUP BY?

Edit

Saya baru belajar saya dapat menggunakan OPTION(querytraceon 8649) untuk mengatur biaya paralelisme ke 0, yang membuat kueri menggunakan paralelisme dan mengurangi runtime menjadi 2 detik, meskipun saya tidak tahu apakah ada kerugian untuk menggunakan petunjuk kueri ini.

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

enter image description here

Saya masih lebih suka runtime yang lebih pendek karena kueri dimaksudkan untuk mengisi nilai pada pilihan pengguna, jadi idealnya seketika seperti kueri yang dikelompokkan. Saat ini saya hanya membungkus pertanyaan saya, tetapi saya tahu itu bukan solusi yang ideal.

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

Edit # 2

Menanggapi permintaan Martin untuk info lebih lanjut :

Baik CreatedDate dan SomeIndexedValue memiliki indeks non-unik, non-clustered terpisah pada mereka. SomeIndexedValue sebenarnya adalah bidang varchar (7), meskipun ia menyimpan nilai numerik yang menunjuk ke PK (int) dari tabel lain. Hubungan antara dua tabel tidak didefinisikan dalam database. Saya tidak seharusnya mengubah database sama sekali, dan hanya dapat menulis kueri yang meminta data.

MyTable berisi lebih dari 3 juta catatan, dan setiap catatan ditetapkan ke grup yang menjadi tempatnya (SomeIndexedValue). Grup dapat berkisar dari 1 hingga 200.000 catatan

12
Rachel

Sepertinya ini mungkin mengikuti indeks pada CreatedDate dalam urutan dari terendah ke tertinggi dan melakukan pencarian untuk mengevaluasi SomeIndexedValue = 1 predikat.

Ketika menemukan baris pencocokan pertama selesai, tetapi mungkin melakukan lebih banyak pencarian daripada yang diharapkan sebelum menemukan baris seperti itu (mengasumsikan baris yang cocok dengan predikat didistribusikan secara acak sesuai tanggal.)

Lihat jawaban saya di sini untuk masalah yang sama

Indeks ideal untuk kueri ini adalah indeks SomeIndexedValue, CreatedDate. Dengan asumsi bahwa Anda tidak dapat menambahkan itu atau setidaknya membuat indeks yang ada pada SomeIndexedValue cover CreatedDate sebagai kolom yang disertakan maka Anda dapat mencoba menulis ulang kueri sebagai berikut

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

untuk mencegahnya menggunakan rencana khusus itu.

8
Martin Smith

Bisakah kita mengontrol MAXDOP dan memilih tabel yang diketahui, mis., AdventureWorks.Production.TransactionHistory?

Ketika saya ulangi pengaturan Anda menggunakan

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

biayanya identik.

Sebagai tambahan, saya akan mengharapkan (mewujudkannya) indeks mencari nilai indeks Anda; jika tidak, Anda cenderung akan melihat kecocokan hash alih-alih agregat aliran. Anda dapat meningkatkan kinerja dengan indeks yang tidak berkerumun yang menyertakan nilai yang Anda agregat dan atau membuat tampilan yang diindeks yang mendefinisikan agregat Anda sebagai kolom. Maka Anda akan memukul indeks berkerumun, yang berisi agregasi Anda, dengan Id Terindeks. Di SQL Standard, Anda bisa membuat tampilan dan menggunakan petunjuk WITH (NOEXPAND).

Contoh (saya tidak menggunakan MIN, karena tidak berfungsi dalam tampilan yang diindeks):

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
2
ooutwire

Menurut pendapat saya alasan untuk masalah ini adalah bahwa pengoptimal server sql tidak mencari rencana TERBAIK melainkan mencari rencana yang baik, seperti terbukti dari fakta bahwa setelah memaksa paralelisme kueri dieksekusi lebih cepat, sesuatu yang dimiliki pengoptimal memiliki tidak dilakukan sendiri.

Saya juga telah melihat banyak situasi di mana penulisan ulang kueri dalam format yang berbeda adalah perbedaan antara paralelisasi (misalnya meskipun sebagian besar artikel tentang SQL merekomendasikan parameterisasi saya telah menemukan hal itu menyebabkan kadang-kadang tidak dapat menyejajarkan bahkan ketika parameter mengendus sama dengan non - diparalelkan, atau menggabungkan dua pertanyaan dengan UNION ALL terkadang dapat menghilangkan paralelisasi).

Dengan demikian solusi yang tepat mungkin dengan mencoba berbagai cara penulisan kueri, seperti mencoba tabel temp, variabel tabel, cte, tabel turunan, parameterisasi, dan sebagainya, dan juga bermain dengan indeks, tampilan indeks, atau indeks yang difilter dalam memesan untuk mendapatkan rencana terbaik.

0
yoel halb