it-swarm.asia

لماذا يكون الاستعلام المجمع أسرع بكثير مع عبارة GROUP BY من دون واحد؟

أنا فقط أشعر بالفضول لماذا يعمل الاستعلام الكلي بشكل أسرع بكثير مع عبارة GROUP BY من دون واحد.

على سبيل المثال ، يستغرق هذا الاستعلام حوالي 10 ثوانٍ ليعمل

SELECT MIN(CreatedDate)
FROM MyTable
WHERE SomeIndexedValue = 1

في حين أن هذا يستغرق أقل من ثانية

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

لا يوجد سوى CreatedDate واحد في هذه الحالة ، لذا فإن الاستعلام المجمّع يُرجع نفس النتائج مثل غير المفرد.

لاحظت أن خطط التنفيذ للاستعلامين مختلفة - الاستعلام الثاني يستخدم التوازي بينما لا يفعل الاستعلام الأول.

Query1 Execution PlanQuery2 Execution Plan

هل من الطبيعي أن يقوم خادم SQL بتقييم استعلام مجمع بشكل مختلف إذا لم يكن لديه جملة GROUP BY؟ وهل هناك شيء يمكنني القيام به لتحسين أداء الاستعلام الأول دون استخدام عبارة GROUP BY؟

تحرير

لقد علمت للتو أنه يمكنني استخدام OPTION(querytraceon 8649) لتعيين التكلفة العامة للتوازي على 0 ، مما يجعل الاستعلام يستخدم بعض التوازي ويقلل وقت التشغيل إلى ثانيتين ، على الرغم من أنني لا أعرف ما إذا كان هناك أي سلبيات باستخدام تلميح الاستعلام هذا.

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

enter image description here

ما زلت أفضل وقت تشغيل أقصر نظرًا لأن الاستعلام يهدف إلى تعبئة قيمة عند تحديد المستخدم ، لذلك يجب أن يكون فوريًا مثل الاستعلام المجمّع. أقوم الآن بتغليف الاستعلام الخاص بي ، لكنني أعلم أن هذا ليس حلاً مثاليًا حقًا.

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

تعديل رقم 2

رداً على طلب مارتن لمزيد من المعلومات :

يحتوي كل من CreatedDate و SomeIndexedValue على فهرس منفصل غير فريد وغير مجمع. SomeIndexedValue هو حقًا حقل varchar (7) ، على الرغم من أنه يخزن قيمة رقمية تشير إلى PK (int) لجدول آخر. لم يتم تعريف العلاقة بين الجدولين في قاعدة البيانات. ليس من المفترض أن أغير قاعدة البيانات على الإطلاق ، ولا يسعني إلا كتابة الاستعلامات التي تبحث عن البيانات.

MyTable يحتوي على أكثر من 3 ملايين سجل ، ويتم تعيين كل سجل لمجموعة تنتمي إليها (SomeIndexedValue). يمكن أن تكون المجموعات في أي مكان من 1 إلى 200000 سجل

12
Rachel

يبدو أنه من المحتمل أنه يتبع مؤشرًا على CreatedDate بالترتيب من الأدنى إلى الأعلى ويقوم بعمليات بحث لتقييم SomeIndexedValue = 1 فاعل.

عندما يعثر على الصف المطابق الأول ، يتم ذلك ، ولكنه قد يقوم بعمليات بحث أكثر مما يتوقع قبل أن يجد مثل هذا الصف (يفترض أن الصفوف التي تطابق المسند يتم توزيعها عشوائيًا وفقًا للتاريخ.)

انظر جوابي هنا لمشكلة مماثلة

سيكون الفهرس المثالي لهذا الاستعلام واحدًا على SomeIndexedValue, CreatedDate. بافتراض أنه لا يمكنك إضافة ذلك أو على الأقل إنشاء فهرسك الحالي على SomeIndexedValue cover CreatedDate كعمود مضمّن ، يمكنك محاولة إعادة كتابة الاستعلام على النحو التالي

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

لمنعها من استخدام تلك الخطة المعينة.

8
Martin Smith

هل يمكننا التحكم في 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 

التكاليف متطابقة.

جانبا ، أتوقع (تحقيق ذلك) فهرس يبحث عن القيمة المفهرسة الخاصة بك ؛ وإلا ، فمن المحتمل أن ترى تطابقات التجزئة بدلاً من مجموعات الدفق. يمكنك تحسين الأداء باستخدام الفهارس غير المجمعة التي تتضمن القيم التي تقوم بتجميعها أو إنشاء طريقة عرض مفهرسة تحدد التجميعات على أنها أعمدة. ثم ستصطدم بفهرس مجمع يحتوي على مجموعاتك بواسطة معرف مفهرس. في 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
2
ooutwire

في رأيي ، فإن سبب المشكلة هو أن مُحسِّن خادم sql لا يبحث عن أفضل خطة ، بل إنه يبحث عن خطة جيدة ، كما هو واضح من حقيقة أنه بعد فرض التوازي تم تنفيذ الاستعلام بشكل أسرع ، وهو الأمر الذي كان للمحسن لم يتم بمفرده.

لقد رأيت أيضًا العديد من المواقف التي كانت فيها إعادة كتابة الاستعلام بتنسيق مختلف هو الفرق بين التوازي (على سبيل المثال على الرغم من أن معظم المقالات في SQL توصي بتعيين المعلمات ، فقد وجدت أنه يتسبب أحيانًا في موازاة التوازي حتى عندما تكون المعلمات متشابهة مثل - واحد متوازي ، أو الجمع بين استعلامين مع UNION ALL يمكن في بعض الأحيان القضاء على التوازي).

على هذا النحو قد يكون الحل الصحيح من خلال تجربة طرق مختلفة لكتابة الاستعلام ، مثل تجربة الجداول المؤقتة ، ومتغيرات الجدول ، cte ، والجداول المشتقة ، والمعلمات ، وما إلى ذلك ، واللعب أيضًا مع الفهارس أو طرق العرض المفهرسة أو الفهارس المصفاة في من أجل الحصول على أفضل خطة.

0
yoel halb