Yüksek seçicilik ve düşük seçicilik alanlarına sahip bileşik bir dizin düzeninde alan sırası


11

3 milyardan fazla satır içeren bir SQL Server tablo var. Bir benim sorgu son derece uzun zaman alır, bu yüzden onu optimize düşünüyorum. Sorgu şöyle görünür:

SELECT [Enroll_Date]
      ,Count(*) AS [Record #]
      ,Count(Distinct UserID) AS [User #]
  FROM UserTable
  GROUP BY [Enroll_Date]

[Enroll_Date], 50'den az olası değere sahip düşük seçiciliğe sahipken UserID sütunu 200 milyondan fazla farklı değere sahip yüksek seçiciliğe sahip bir sütundur. Araştırmamı temel alarak bu iki sütun üzerinde kümelenmemiş bir bileşik dizin oluşturmam gerektiğine inanıyorum ve teoride yüksek seçicilik sütunu ilk sütun olmalı. Ama benim durumumda, bu işe yarayacağından emin değilim çünkü gruptaki düşük seçicilik sütununu yan tümce olarak kullanıyorum.

Bu tablonun kümelenmiş dizini yok.


Gerçek yürütme planı xml'yi gönderebilir misiniz (pastebin kullanın ve buraya bağlayın)? Hangi sql sunucusunun sürümünü kullanıyorsunuz?
Kin Shah

3
İlk olarak son derece seçici sütunu olan dizin, belirli bir sorgu için işe yaramaz.
ypercubeᵀᴹ

Bir dizindeki ilk anahtar sütun olarak (normalde) daha yüksek seçicilik sütununu kullanmak en iyi uygulamadır. Bu senaryoda, tahmin ettiğiniz gibi, size hiç yardımcı olmuyor. İki dizine ihtiyacınız olabilir! Önce enroll_date, ikinci olarak user_id kullandığınızda ne olur?
paulbarbin

Yanıtlar:


12

@ AaronBertrand'ın çözümüne alternatif olarak (dizine alınmış bir görünüm oluşturmak istemiyorsanız veya istemiyorsanız), üzerinde bir dizin oluşturmanızı öneririm (Enroll_Date, UserID). Bu tür bir soru tablonuzda çok yaygınsa, bu muhtemelen kümelenmiş dizininiz olmalıdır.

Genel olarak yüksek seçiciliğe sahip dizinleri genel bir "en iyi uygulama" olarak önermem, bunun yerine sorgunuza en iyi performansı hangi dizine vereceğine bakarım.

Tarihinde bir dizin, (Enroll_Date, UserID)sorgunuza Akış Toplamaları ile oldukça optimize edilmiş, engellenmeyen bir sorgu planı verecektir.

Akış toplu sorgu planı

Bu bağlamda "engellenmemesi", sorgunun önemli miktarda veri (örneğin bir sıralama veya karma toplamı gibi) arabelleğe alması gerekmediği anlamına gelir. b) neredeyse hiç çalışma belleği tüketmez.


Komik, 4 saniye arayla ve aynı cevap.
usr

11

Aarons'un cevabı harika bir çözüm. Bu yaklaşımı benimsemek istemediğinizi varsayarak soruyu cevaplayacağım.

Gönderdiğiniz sorgu genellikle önce gruplandırıldıktan (Enroll_Date, UserID)sonra tekrar gruplandırılarak yürütülür (Enroll_Date). Bu optimizasyon SQL Server 2012 için yenidir COUNT DISTINCT.

Belirli bir sırayla bu iki sütundaki bir dizin, (Enroll_Date, UserID)bir dizin taramasını art arda iki Akış Topluluğuna yönlendiren etkili bir plan elde etmek için yeterli olacaktır. Tersi emir bu planı mümkün kılmaz.

Bu nedenle, siparişi kullanın (Enroll_Date, UserID). Burada baţka seçeneđin yok.


5 saniye arayla ve aynı çözelti. İyi oynadın, efendim. :)
Daniel Hutmacher

@DanielHutmacher OMG, yayınlarımıza neredeyse 3. kez eşleşmeyi başarabilecek miyiz ?! Sana +1! Nasıl olabilir değil aynı cevabı upvote?
usr

Matristeki Aksaklık. :)
Daniel Hutmacher

Çok teşekkür ederim. Dizin oluşturuyorum ve iyileştirme tamamlandıktan sonra yayınlayacağım. Sunucu sürümü AWS üzerinde Microsoft SQL Server 2008 R2, ama sanırım yine de tek choince.
Thinkinger

@Aarons yaklaşımını kabul etmemeniz durumunda zor bir seçiminiz var :)
usr

11

Sorgu zamanı yerine yazma zamanında hesaplamalar ve toplamalar için ödeme yapmanıza olanak tanıyan dizine alınmış bir görünüm için ideal bir senaryo gibi görünür.

CREATE VIEW dbo.MyIndexedView
WITH SCHEMABINDING
AS 
  SELECT Enroll_Date, UserID, RawCount = COUNT_BIG(*)
  FROM dbo.UserTable
  GROUP BY Enroll_Date, UserID;
GO

CREATE UNIQUE CLUSTERED INDEX CIX_miv ON dbo.MyIndexedView(Enroll_Date, UserID);

Bunun oluşturulması biraz zaman alacak ve elbette, temel tablodaki bir dizin gibi, tüm DML işlemlerinde bakım gerektirecektir.

Şimdi bu görünüme karşı sorgu oldukça benzer olacaktır - görünümdeki her satır şimdi ayrı bir kullanıcı / tarih kombinasyonunu temsil eder, böylece bu sayı tek bir COUNT (*) ile hesaplanabilirken, temel tablodaki toplam satır sayısı zaten sizin için kısmen toplanmıştır, şimdi bunları tarih başına SUM kullanarak eklemeniz yeterlidir:

SELECT Enroll_Date, 
  [Record #] = SUM(RawCount),
  [User #] = COUNT(*)
FROM dbo.MyIndexedView WITH (NOEXPAND)
GROUP BY Enroll_Date; 

Bunu ve bunu hatırladıktan sonra NOEXPAND ipucu eklendi .

Hiç şüphesiz bu sorgunun her bir sorgu için tam olarak bir kullanıcınızın olduğu nadir durumlarda (bu durumda aynı miktarda veri olacaktır) okunacak) ve bildiğimiz sütunlar temel tablonun dizinindeki tek sütunlardır. Okuma zamanındaki performans artışının, iş yükünüzün yazma kısmını etkileyecek ekstra çalışmaya değip değmeyeceği, size söyleyemeyeceğimiz bir şeydir - ödünleşimi ölçmek için test etmeniz gerekir (hiçbir dizin ücretsiz değildir).

Ayrıca, belirli, iyi tanımlanmış aralıklar (örneğin, geçerli çeyrek veya bugüne kadar) için Enroll_Date'e karşı aynı ortak WHERE yan tümcelerini sık sık kullanıyorsanız, G / Ç'yi daha da azaltan eşleşen filtrelenmiş dizinler ekleyebilirsiniz (ancak her zaman Pazarlıksız).

Ayrıca temel tabloya kümelenmiş bir dizin koymayı düşünebilirsiniz. Bu, bir yığından yararlanan çok nadir kullanım durumlarından biri gibi görünmüyor.


BT'mizle doğruladım ve bu tür bir görüş yaratamayacağım anlaşılıyor. Ancak yine de tavsiyenizi doğrulayın ve bunu kullanabilen başkalarına yardımcı olacaktır.
Thinkinger

1
BT'niz, dizinlenmiş bir görünüm ile temel tablodaki ek veya farklı dizinler arasında önemli bir fark olduğunu düşünüyor mu? Savaşçı olmamak, sadece merak etmek, çünkü birçok insanın endekslenmiş görüşlerle ilgili yanılgıları var. Onları masada ek, daha ince bir kümelenmiş dizin olarak düşünmeyi seviyorum, ancak daha az satır var.
Aaron Bertrand

@Thinkinger ayrıca, dizinlenmiş görünümler yalnızca EE değildir. Dizine eklenen görünüm eşleşmesi yalnızca EE'dir. NOEXPAND kullanarak doğrudan hedefleyebilirsiniz.
usr
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.