2012-02-09 25 views
11

Yüzlerce , satır binlerce ile "Arabalar" bir tablo vardı varsayalım COUNT gelen MAX yer ve GROUP BY yapmak istedim:t-sql COUNT ile GROUP BY ve sonra

SELECT CarID 
     , CarName 
     , COUNT(*) AS Total 
FROM  dbo.tbl_Cars 
GROUP BY CarID 
     , CarName 

gruplama benzer bir sonuçla bırakır:

CarID  CarName Total 
1872  Olds  202,121 
547841  BMW  175,298 
9877  Ford  10,241 

tüm ince ve iyi. Benim sorum olsa da, performans ve temiz kodlama açısından Toplam ve bir tabloya MAX Toplam almak için en iyi yolu nedir, bu nedenle böyle bir sonucu vardır:

CarID  CarName Total  Max Total 
1872  Olds  202,121 202,121 
547841  BMW  175,298 202,121 
9877  Ford  10,241 202,121 

Bir yaklaşıma GRUP sonucunu geçici tabloya, 'a koymak ve MAX'ı geçici tablodan yerel bir değişkene almak olacaktır. Ama bunu yapmanın en iyi yolunun ne olacağını merak ediyorum.


GÜNCELLEME

Ortak Tablo İfade yazmak için en şık görünüyor, @EBarr henüz benzer , benim sınırlı test önemli ölçüde daha yavaş bir performans gösterir. Yani CTE ile gitmeyeceğim.

@EBarr bağlantısının COMPUTE için bağlantı seçeneği olarak öğesinin kullanımdan kaldırıldığını bildirir, bu da en iyi yol görünmüyor.

MAX değeri ve bir geçici tablonun kullanımı için yerel bir değişken seçeneği, aşağı yukarı gideceğim yoldur, çünkü ben ile ilgili performans sorunlarının farkında değilim.

A benim kullanım durumunda ilgili daha fazla ayrıntı bit: muhtemelen diğer SO soru serisi olmuyor olabilir. Ancak, büyük bir veri kümesini geçici bir tabloya yüklediğimi söylemek yeterlidir (bu yüzden tbl_Cars'ın bir alt kümesi, #tbl_Cars içine giriyor ve hatta #tbl_Cars daha fazla filtrelenebilir ve üzerinde gerçekleştirilebilir). Ben birden çok sonuç kümesi döndüren bir tek saklı yordam geçmesine rağmen üzerlerinde çoklu filtreleme ve toplama sorguları gerçekleştirmek zorunda. pencereli bir fonksiyonun EBarr kullanması güzel ve kısa @


GÜNCELLEME 2

. Not: , bir dış başvuru tablosuna RIGHT JOIN kullanılıyorsa, COUNT() işlevi, '*' değil tbl_Cars'dan bir sütun seçmelidir. Hız açısından

SELECT  M.MachineID 
      , M.MachineType 
      , COUNT(C.CarID) AS Total 
      , MAX(COUNT(C.CarID)) OVER() as MaxTotal 
FROM   dbo.tbl_Cars C 
RIGHT JOIN dbo.tbl_Machines M 
     ON  C.CarID = M.CarID 
GROUP BY  M.MachineID 
      , M.MachineType 

, bu iyi görünüyor, ama ne noktada okur sayısı hakkında endişeli olmak zorunda mısın?

cevap

13

mekanik bunu yapmanın birkaç yolu vardır. Temp tabloları/tablo değişkenini kullanabilirsiniz. Diğer bir yol ise iç içe geçmiş sorgularla ve/veya @Aaron_Bertrand'ın gösterdiği gibi bir CTE'dir. Üçüncü bir yol gibi WINDOWED FONKSİYONLARI kullanmaktır ...

SELECT CarName, 
      COUNT(*) as theCount, 
      MAX(Count(*)) OVER(PARTITION BY 'foo') as MaxPerGroup 
FROM  dbo.tbl_Cars 
GROUP BY CarName 

bir rağbet edilmeyen (BULUNMADI okuyun) dördüncü yol

SELECT CarID, CarName, Count(*) 
FROM  dbo.tbl_Cars 
GROUP BY CarID, CarName 
COMPUTE MAX(Count(*)) 

COMPUTE kelime sonunda ek özet sütun görünür toplamları oluşturur ... gibi HESAPLAMA anahtar kelimeyi kullanarak sonuç kümesi (see this). Yukarıdaki sorguda aslında iki kayıt kümesini göreceksiniz.

En hızlı

Şimdi, bir sonraki konu budur "en iyi/en hızlı/en kolay." Hemen bir indexed view düşünün. @Aaron'un bana nazikçe hatırlattığı gibi, endeksli görünümler her türlü kısıtlamaya sahip. Ancak yukarıdaki strateji, SELECT ... FROM..GROUP BY. Üzerinde endeksli bir görünüm oluşturmanıza izin verir. Dizinlenmiş görünümden seçim yapmak WINDOWED FUNCTION deyimini uygular.

Daha fazla bilgi sahibi olmasa da, tasarımınız hakkında, kimsenin size en iyi olanı söylemesi zor olacaktır. Dizinlenmiş bir görünümden hızlı sorguları alacaksınız. Ancak bu performans bir fiyata geliyor. Fiyat bakım masraflarıdır. Alttaki tablo büyük miktarda ekleme/güncelleştirme/silme işleminin hedefi ise, dizinlenmiş görünümün bakımı diğer alanlarda performansı azaltır.

Kullanım durumunuz ve veri erişim düzenleriniz hakkında biraz daha fazla bilgi paylaşıyorsanız, insanlar daha fazla bilgi paylaşabilecekler.


MİKRO PERFORMANS TESTİ

Yani küçük veri senaryoyu oluşturulur ve pencereli fonksiyonları vs CTE performansı için sql profiler numaraları baktı. Bu bir mikro-testtir, bu yüzden sisteminizdeki gerçek sayıları gerçek yük altındaki gerçek sayıları deneyin.

Veri nesil:

Create table Cars (CarID int identity (1,1) primary key, 
        CarName varchar(20), 
        value int) 
GO 
insert into Cars (CarName, value) 
values ('Buick', 100), 
     ('Ford', 10), 
     ('Buick', 300),  
     ('Buick', 100), 
     ('Pontiac', 300),  
     ('Bmw', 100), 
     ('Mecedes', 300),  
     ('Chevy', 300),  
     ('Buick', 100), 
     ('Ford', 200); 
GO 1000 

Bu komut dosyası 10.000 satır üretir. Sonra dört aşağıdaki sorguları birden çok kez her ran: Yukarıdaki sorguları çalıştırdıktan sonra

--just group by 
select CarName,COUNT(*) countThis 
FROM Cars 
GROUP BY CarName   

--group by with compute (BAD BAD DEVELOPER!) 
select CarName,COUNT(*) countThis 
FROM Cars 
GROUP BY CarName   
COMPUTE MAX(Count(*)); 

-- windowed aggregates... 
SELECT CarName, 
     COUNT(*) as theCount, 
     MAX(Count(*)) OVER(PARTITION BY 'foo') as MaxInAnyGroup 
FROM Cars 
GROUP BY CarName   

--CTE version 
;WITH x AS (
    SELECT CarName, 
      COUNT(*) AS Total 
    FROM  Cars 
    GROUP BY CarName 
) 
SELECT x.CarName, x.Total, x2.[Max Total] 
FROM x CROSS JOIN (
    SELECT [Max Total] = MAX(Total) FROM x 
) AS x2; 

, yukarıdaki sorguda "ile sadece grubun" konulu bir dizinli görünüm yarattı. Ardından, MAX(Count(*)) OVER(PARTITION BY 'foo' gerçekleştiren dizinlenmiş görünümde bir sorgu çalıştırdım.

ORTALAMA Açıkçası

Query      CPU  Reads  Duration 
-------------------------------------------------------- 
Group By     15  31  7 ms 
Group & Compute   15  31  7 ms 
Windowed Functions   14  56  8 ms 
Common Table Exp.   16  62  15 ms 
Windowed on Indexed View 0  24  0 ms 

SONUÇLARI bu mikro kriter ve sadece hafif öğreticidir, yani buna değer ne için götürün.

;WITH x AS 
(
    SELECT CarID 
     , CarName 
     , COUNT(*) AS Total 
    FROM  dbo.tbl_Cars 
    GROUP BY CarID, CarName 
) 
SELECT x.CarID, x.CarName, x.Total, x2.[Max Total] 
FROM x CROSS JOIN 
(
    SELECT [Max Total] = MAX(Total) FROM x 
) AS x2; 
+0

Bir endeksli görünümünde 'MAX' kullanamaz (ı been 5 yıl için soruyorum - http://connect.microsoft.com/SQLServer/feedback/details/267516/expand-aggregate-support-in-indexed-views-min-max). Ayrıca "theFieldBeingSearchedForMax", tabloda yok, çıktının bir parçasıdır (en yüksek sayıdır). –

+0

Sadece tekrar soruyu okuyun. Yanlış okumuşum. SQL güncelleniyor. – EBarr

+0

- Üzgünüz, ilk sorguda GROUP BY'yi ekleyemedim; benim hatam. – mg1075

8

İşte bir yolu

GROUP BY CarID, CarName WITH ROLLUP 
0

SQL Server 2008 R2 ve daha yeni sürümleri kullanabilirsiniz: