Saymak (*) ve Saymak (1) - SQL Server


738

İnsanların herhangi birini kullanıyorsanız sadece merak Count(1)üzerinde Count(*)ve performans veya eğer bir fark varsa bu gitti Geçtiğimiz günlerde öne getirilir olmuştur sadece eski alışkanlıktır?

Belirli veritabanı SQL Server 2005.


7
SQL Server hakkında bilmiyorum ama MySQL'de fark yok. Öte yandan COUNT (sütun) farklı
Greg

118
Doğru değil. COUNT (SomeColumn) yalnızca SomeColumn için null olmayan değerler içeren satırların sayısını döndürür. COUNT (*) ve COUNT ('Foo') tablodaki toplam satır sayısını döndürür.
Steve Broberg


4
Vay canına Steve ve burada Count (ColumnName) vs count (*) bilmeden TSQL 5 yıl vardı. Teşekkürler
Harindaka

Yanıtlar:


598

Fark yok.

Sebep:

Çevrimiçi kitaplar " COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )" diyor

"1" null olmayan bir ifadedir: bu yüzden ile aynıdır COUNT(*). Optimize edici onu ne olduğu için tanır: önemsiz.

EXISTS (SELECT * ...Veya ile aynıEXISTS (SELECT 1 ...

Misal:

SELECT COUNT(1) FROM dbo.tab800krows
SELECT COUNT(1),FKID FROM dbo.tab800krows GROUP BY FKID

SELECT COUNT(*) FROM dbo.tab800krows
SELECT COUNT(*),FKID FROM dbo.tab800krows GROUP BY FKID

Aynı ES, aynı plan, işler

Edit, Ağu 2011

DBA.SE üzerinde benzer bir soru .

Edit, Ara 2011

COUNT(*)ANSI-92'de özellikle belirtilmiştir (" Scalar expressions 125" ifadesini arayın )

Durum:

a) COUNT (*) belirtilirse, sonuç T'nin önemidir.

Yani, ANSI standardı bunu kastettiğinizin kanaması olarak kabul eder. bu batıl inanç nedeniyleCOUNT(1) RDBMS satıcıları tarafından optimize edilmiştir . Aksi takdirde ANSI'ye göre değerlendirilir

b) Aksi takdirde, TX'in her T satırına <değer ifadesi> uygulanmasının ve boş değerlerin kaldırılmasının sonucu olan tek sütunlu tablo olmasına izin verin. Bir veya daha fazla boş değer elimine edilirse, bir tamamlama koşulu ortaya çıkar: uyarı-


73

SQL Server'da, bu ifadeler aynı planları verir.

Popüler düşüncenin aksine, Oracle'da da öyle.

SYS_GUID() Oracle'da oldukça hesaplama yoğun bir işlevdir.

Test veritabanımda satır t_eveniçeren bir tablo var1,000,000

Bu sorgu:

SELECT  COUNT(SYS_GUID())
FROM    t_even

48saniye boyunca çalışır , çünkü işlevin SYS_GUID()döndürülmediğinden emin olmak için döndürülen her bir değeri değerlendirmesi gerekir NULL.

Ancak, bu sorgu:

SELECT  COUNT(*)
FROM    (
        SELECT  SYS_GUID()
        FROM    t_even
        )

fakat için çalışır 2o değerlendirmek için denemek bile doent itibaren saniye, SYS_GUID()(rağmen *argüman olmaktan COUNT(*))


SYS_GUID()alt sorgunun sonucu döndürmesi için en azından (yani, tam olarak) bir kez değerlendirmesi gerekir , değil mi?
asgs

@asgs: neden böyle düşünüyorsun? COUNT(*)Değerlerine nasıl bağlıdır SYS_GUID?
Quassnoi

şimdi sorduğunuza emin değilim. COUNT(*)Çalıştırmak için düşündüm , bir tabloya ihtiyacı var, bu yüzden alt sorgu bir gibi davranmalıdır. Aksi takdirde, COUNT(*)anlamlı bir değer döndürmenin bir yolunu göremiyorum
asgs

1
@asgs: mapyöntemin ne yaptığını bildiğinizi varsayarsak , bu iki ifadenin nasıl olduğunu görüyor musunuz: t_even.map(() => sys_guid()).lengthve t_even.lengthher zaman aynı değeri döndürür mü? Oracle'ın optimize edicisi, mapparçayı görebilecek ve optimize edecek kadar akıllıdır .
Quassnoi

1
@asgs tam olarak. Sadece küçük bir düzeltme: lengthOldukça bağlı değildir neyi koleksiyon sadece kendi elemanlarının sayısı, oluşur. Bu sayı koleksiyonun meta verilerinde depolanırsa (bu Oracle veya diğer birçok modern RDBMS için geçerli değildir, ancak eski MySQL'in depolama motoru MyISAM için geçerliyse), COUNT(*)değeri meta verilerden almanız gerekir.
Quassnoi

65

Açıkçası COUNT(*)ve COUNT(1)her zaman aynı sonucu döndürür. Bu nedenle, biri diğerinden daha yavaş olsaydı, bir optimizasyon hatası etkili olurdu. Her iki form da sorgularda çok sık kullanıldığından, DBMS'nin böyle bir hatanın düzeltilmemesine izin vermesi mantıklı olmaz. Bu nedenle, her iki formun performansının tüm büyük SQL DBMS'lerde (muhtemelen) aynı olduğunu göreceksiniz.


Kont (1) sayısı (*) 'dan daha yavaş olsaydı bunu bir hata olarak görmezdim. Dbms'den 1s oluşturmasını ve null olmayanları saymasını isterseniz, evet, kayıt sayısı olarak kaynar, ancak dbms'in yazdığınız her saçmalığı algılamasını ve sizin için atlatmasını bekleyemezsiniz.
Thorsten Kettner

1
Optimize edici, optimize etmek içindir ve bir sayı için dikkate alınması gereken sadece 2 durum vardır: boş olabilecek ifade, asla boş olmayacak ifade: count (1) ikinciye düşer, böylece DBMS'nin soruyu cevaplamak için "oluşturmak" 1'ler. (BTW Ben sadece saymaktan başka bir şey kullanmam (*), sadece estetik nedenlerle.)
Tony Andrews

46

SQL Server ekibi üzerinde çalışıyorum ve umarım bu konudaki birkaç noktayı açıklığa kavuşturabilirim (daha önce görmemiştim, bu yüzden mühendislik ekibinin daha önce yapmadığı için üzgünüm).

İlk olarak, select count(1) from tablevs. arasında anlamsal bir fark yoktur select count(*) from table. Her durumda aynı sonuçları döndürürler (ve değilse bir hatadır). Diğer cevaplarda belirtildiği gibi select count(column) from table, anlamsal olarak farklıdır ve her zaman aynı sonuçları döndürmez count(*).

İkincisi, performans açısından SQL Server'da (ve SQL Azure'da) önemli olan iki unsur vardır: derleme zamanı çalışması ve yürütme zamanı çalışması. Derleme zamanı işi, mevcut uygulamada çok az miktarda ekstra çalışmadır. Bazı durumlarda * 'ın tüm sütunlara genişlemesi, ardından bazı dahili işlemlerin bağlanma ve optimizasyonda nasıl çalıştığından dolayı çıkacak 1 sütuna kadar bir azalma olur. Ölçülebilir bir testte görüneceğinden ve muhtemelen kapakların altında gerçekleşen diğer tüm şeylerin (otomatik istatistikler, xevent oturumları, sorgu deposu yükü, tetikleyiciler vb.) Gürültüsünde kaybolacağından şüpheliyim. Belki birkaç bin ekstra CPU talimatıdır. Yani, count (1) derleme sırasında biraz daha az iş yapar (genellikle bir kez olur ve plan birden fazla yürütmede önbelleğe alınır). Yürütme süresi için planların aynı olduğu varsayılarak ölçülebilir bir fark olmamalıdır. (Önceki örneklerden biri bir fark gösterir - büyük olasılıkla, plan aynıysa makinedeki diğer faktörlerden kaynaklanır).

Planın potansiyel olarak nasıl farklı olabileceğine gelince. Bunların olması pek olası değildir, ancak mevcut optimize edicinin mimarisinde potansiyel olarak mümkündür. SQL Server'ın optimizer bir arama programı olarak çalışır (düşünün: sorgunun farklı bölümleri için çeşitli alternatifler aracılığıyla satranç oynayan ve makul zamanda en ucuz planı bulmak için alternatifleri maliyetlendiren bilgisayar programı). Bu aramanın, sorgu derleme işlemini makul sürede bitirmesini sağlamak için nasıl çalıştığı konusunda birkaç sınırı vardır. En önemsiz sorgular için, aramanın aşamaları vardır ve bunlar, optimize edicinin sorgunun potansiyel olarak yürütmek için ne kadar pahalı olduğunu düşündüğüne göre sorgu yığınlarıyla ilgilenir. 3 ana arama aşaması vardır ve her aşama önceki çözümlerden daha ucuz bir plan bulmaya çalışırken daha agresif (pahalı) sezgisel tarama yapabilir. Sonuçta, her aşamanın sonunda, şimdiye kadar bulduğu planı geri getirip getirmeyeceğini veya aramaya devam edip etmeyeceğini belirlemeye çalışan bir karar süreci vardır. Bu süreç şimdiye kadar alınan toplam süreyi ve şimdiye kadar bulunan en iyi planın tahmini maliyetini kullanmaktadır. Bu nedenle, farklı CPU hızlarına sahip farklı makinelerde, daha önceki bir aşamada bir planla bir sonraki arama aşamasına devam etmek yerine zaman aşımı nedeniyle farklı planlar elde etmek mümkündür (nadir de olsa). Ayrıca, son aşamadan zamanlama ve makinedeki tüm belleği tüketen çok, çok pahalı sorgularda belleğin tükenmesi ile ilgili birkaç benzer senaryo vardır (genellikle 64 bitlik bir sorun değildir, ancak daha büyük bir endişeydi) 32 bit sunuculara geri dön). Sonuçta, farklı bir plan alırsanız çalışma zamanındaki performans farklı olacaktır. Yapmıyorum

Net-net: Lütfen hiçbiri önemli olmayan herhangi bir pratik formda kullanmak istediğiniz ikisinden hangisini kullanın. (Dürüst olmak gerekirse, SQL'deki performansı bu konunun ötesinde etkileyen çok daha büyük faktörler vardır).

Umarım bu yardımcı olur. Optimize edicinin nasıl çalıştığı hakkında bir kitap bölümü yazdım, ancak buraya göndermenin uygun olup olmadığını bilmiyorum (ondan hala küçük telifler aldığım için inanıyorum). Yani, İngiltere'de SQLBits'de optimize edicinin nasıl yüksek düzeyde çalıştığı hakkında verdiğim bir konuşmaya bir bağlantı yayınlayacağımı yayınlamak yerine, isterseniz aramanın farklı ana aşamalarını biraz daha ayrıntılı olarak görebilirsiniz bunu öğrenmek için. İşte video bağlantısı: https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer


2
Benim inancım 1da aynı genişleme sürecine giriyor. Bunu burada yapılan mükemmel testlere dayandırıyorum stackoverflow.com/questions/1597442/… Ayrıca 1sütun seviyesi izinleri oynatılırken beklenmedik bir şekilde başarısız olan bir sorgunun cevabındaki örneğe bakın
Martin Smith

21

SQL-92 Standardında, COUNT(*)özellikle "tablo ifadesinin temel özelliği" (bir temel tablo, `` VIEW, türetilmiş tablo, CTE, vb. Olabilir) anlamına gelir.

Bence bu COUNT(*)ayrıştırmanın kolay olmasıydı . Başka bir ifade kullanmak için ayrıştırıcının herhangi bir sütuna başvurmadığından emin olması gerekir ( COUNT('a')burada abir değişmez yer ve bir sütun COUNT(a)nerede afarklı sonuçlar verebilir).

Aynı şekilde, COUNT(*)birden fazla satıcının SQL teklifiyle çalışırken faydalı bir beceri olan SQL Standartlarına aşina olan bir insan kodlayıcı tarafından kolayca seçilebilir.

Ayrıca, özel durumda SELECT COUNT(*) FROM MyPersistedTable;, DBMS'nin tablonun kardinalitesi için istatistik tutacağı düşünülmektedir.

Bu nedenle, COUNT(1)ve COUNT(*)anlamsal olarak eşdeğer olduğu için kullanıyorum COUNT(*).


1
SQL-92 metni DBA.SE'deki cevabımdan bağlandı: dba.stackexchange.com/questions/2511/…
gbn


12

Optimize edicinin garip kenar kasaları dışında gerçek bir fark olmadığından emin olmasını beklerdim.

Her şeyde olduğu gibi, anlatmanın tek gerçek yolu özel durumlarınızı ölçmektir.

Her zaman kullandım dedi COUNT(*).


Kabul edilen cevaba göre, bu MS SQL için geçerli değildir - aslında ikisi arasında bir fark yoktur.
David Manheim

10

Bu soru tekrar tekrar ortaya çıktıkça, bir cevap daha var. Yeni başlayanlar için burada "en iyi uygulama" merak ederek bir şeyler eklemek istiyoruz.

SELECT COUNT(*) FROM something kolay bir iş olan kayıtları sayar.

SELECT COUNT(1) FROM something kayıt başına 1 değerini alır ve null olmayan 1'leri sayar, bu da esasen kayıtları sayar, yalnızca daha karmaşıktır.

Bunu söyledikten sonra: İyi dbms, ikinci ifadenin ilk ifadeyle aynı sayıyla sonuçlanacağını ve gereksiz işler yapmamak için yeniden yorumlayacağını fark eder. Bu nedenle, genellikle her iki ifade de aynı yürütme planıyla sonuçlanır ve aynı miktarda zaman alır.

Ancak okunabilirlik açısından ilk ifadeyi kullanmalısınız. Kayıtları saymak istiyorsunuz, bu yüzden ifadeleri değil kayıtları sayın. COUNT (ifade) işlevini yalnızca bir şeyin boş olmayan durumlarını saymak istediğinizde kullanın.


8

8 GB RAM hyper-v kutusundaki SQL Server 2012'de hızlı bir test yaptım. Sonuçları kendiniz görebilirsiniz. Bu testleri çalıştırırken SQL Server Management Studio dışında başka bir pencereli uygulama çalıştırmıyordum.

Tablo şemam:

CREATE TABLE [dbo].[employee](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_employee] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

EmployeeTablodaki toplam kayıt sayısı : 178090131 (~ 178 milyon satır)

İlk Sorgu:

Set Statistics Time On
Go    
Select Count(*) From Employee
Go    
Set Statistics Time Off
Go

İlk Sorgu Sonucu:

 SQL Server parse and compile time: 
 CPU time = 0 ms, elapsed time = 35 ms.

 (1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 10766 ms,  elapsed time = 70265 ms.
 SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

İkinci Sorgu:

    Set Statistics Time On
    Go    
    Select Count(1) From Employee
    Go    
    Set Statistics Time Off
    Go

İkinci Sorgunun Sonucu:

 SQL Server parse and compile time: 
   CPU time = 14 ms, elapsed time = 14 ms.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 11031 ms,  elapsed time = 70182 ms.
 SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

Sorguların yürütüldüğü sırada tam sistem durumuna kolayca atfedilebilen 83 (= 70265 - 70182) milisaniye fark olduğunu fark edebilirsiniz. Ayrıca tek bir çalışma yaptım, bu yüzden birkaç çalışma yapar ve ortalama alırsam bu fark daha doğru olur. Böyle büyük bir veri seti için fark 100 milisaniyeden daha az geliyorsa, iki sorgunun SQL Server Engine tarafından gösterilen herhangi bir performans farkına sahip olmadığı kolayca sonuca varabiliriz.

Not : RAM her iki çalışmada da% 100'e yakın kullanım sağlar. Her iki çalışmaya başlamadan önce SQL Server hizmetini yeniden başlattım.


7
SET STATISTICS TIME ON

select count(1) from MyTable (nolock) -- table containing 1 million records. 

SQL Server Yürütme Süreleri:
CPU süresi = 31 ms, geçen süre = 36 ms.

select count(*) from MyTable (nolock) -- table containing 1 million records. 

SQL Server Yürütme Süreleri:
CPU süresi = 46 ms, geçen süre = 37 ms.

Her seferinde önbelleği temizleyerek yüzlerce kez çalıştırdım .. Sunucu yükü değiştikçe sonuçlar zaman zaman değişir, ancak neredeyse her zaman count(*)daha yüksek işlemci zamanı vardır.


14
Bunu yeniden üretemem. count(*)ve count(1)SQL 2008 örneğimde 4,5 milyon satır içeren bir tablo sayılırken bile birbirinden birkaç ms içinde sonuç döndürür.
Jeff Atwood

2
Bazen, bazı sistemlerde, ifade her zaman daha hızlı çalışır ... çalıştırılma sırasını rasgele seçtiniz mi?
JosephDoggie

@JosephDoggie bu tür ölçümleri / istatistikleri alırken her sorguyu çalıştırmadan önce her zaman SQL Server hizmetini yeniden başlatmalıdır. SQL Server hizmetini yeni başlattığınızda, her çalıştırma tamamen bağımsız hale gelir ve bu nedenle sorgu sırası önemli olmamalıdır. Öte yandan, SQL Server hizmetini yeniden başlatmazsanız ve motor yürütme planlarının önbelleğe alınmasını yaparsa, daha sonra çalıştırılan sorgunun ilki daha hızlı çalışmaması gerekir.
RBT

Karşılaştırma yaparken yürütme sürelerinin tam sorgu planlarına bakması gerekir. Farklılarsa (örneğin, karma toplamı ile sıralama + akış toplamı), sonuçlar karşılaştırılamaz. Bu yüzden, burada daha fazla veri olmadan sonuç çıkarmaya dikkat ediyorum.
Conor Cunningham MSFT

3

Bir yoktur makale olduğunu gösteren COUNT(1)üzerinde Oracle için sadece bir diğer adıdır COUNT(*)bir ile, ispat bu konuda.

Bazı parçaları alıntılayacağım:

Resmi belgede “SQL deyimini yürütmenin en etkili yolunu belirleyen yerleşik veritabanı yazılımı” olarak tanımlanan “Optimize Edici” adı verilen veritabanı yazılımının bir kısmı vardır.

Optimize edicinin bileşenlerinden birine “transformatör” denir; rolü orijinal SQL deyimini daha verimli olabilecek anlamsal olarak eşdeğer bir SQL deyimine yeniden yazmanın avantajlı olup olmadığını belirlemektir.

COUNT (1) kullanarak bir sorgu yazdığınızda optimize edicinin ne yaptığını görmek ister misiniz?

Olan bir kullanıcı ile ALTER SESSIONayrıcalık, bir koyabilirsiniz tracefile_identifier, iyileştirici izlemeyi etkinleştirmek ve çalıştırmak COUNT(1)gibi seçin: SELECT /* test-1 */ COUNT(1) FROM employees;.

Bundan sonra, ne yapılabilir, izleme dosyalarını yerelleştirmeniz gerekir SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace';. Daha sonra dosyada şunları bulacaksınız:

SELECT COUNT(*) COUNT(1)” FROM COURSE”.”EMPLOYEES EMPLOYEES

Gördüğünüz gibi, bu sadece bir takma ad COUNT(*).

Bir başka önemli yorum: Oracle'da yirmi yıl önce , Oracle 7.3'ten önceCOUNT(*) gerçekten daha hızlıydı :

Count (1), 7.3'ten beri count (*) olarak yeniden yazılmıştır, çünkü Oracle efsanevi ifadeleri Otomatik Ayarlamayı sever. Oracle7'nin önceki dönemlerinde, oracle DETERMINISTIC ve DETERMINISTIC olmadan önce her satır için bir fonksiyon olarak (1) değerini değerlendirmek zorundaydı.

Yirmi yıl önce, sayım (*) daha hızlıydı

Sql Server olarak başka veritabanları için, her biri için ayrı ayrı araştırılmalıdır.

Bu sorunun Sql Server için özel olduğunu biliyorum, ancak SO hakkında aynı konu hakkında, veritabanından bahsetmeden, diğer sorular kapatıldı ve bu cevap yinelenen olarak işaretlenmiş.


1

Tüm RDBMS'lerde, iki sayma yolu ürettikleri sonuç açısından eşdeğerdir. Performans ile ilgili olarak, SQL Server'da herhangi bir performans farkı gözlemlemedim, ancak bazı RDBMS'nin, örneğin PostgreSQL 11'in, COUNT(1)argüman ifadesinin nullabilitesini kontrol ettikleri için daha az optimal uygulamalara sahip olduklarını belirtmek faydalı olabilir .

Çalışırken 1 milyon satır için% 10'luk bir performans farkı buldum:

-- Faster
SELECT COUNT(*) FROM t;

-- 10% slower
SELECT COUNT(1) FROM t;

0

COUNT (1), hiç değilse COUNT (*) öğesinden önemli ölçüde farklı değildir. BOŞALTMA GEÇERSİZ KOLONLAR sorusuna gelince, bu COUNT (*) ve COUNT (<some col>) arasındaki farkları göstermek için basit olabilir -

USE tempdb;
GO

IF OBJECT_ID( N'dbo.Blitzen', N'U') IS NOT NULL DROP TABLE dbo.Blitzen;
GO

CREATE TABLE dbo.Blitzen (ID INT NULL, Somelala CHAR(1) NULL);

INSERT dbo.Blitzen SELECT 1, 'A';
INSERT dbo.Blitzen SELECT NULL, NULL;
INSERT dbo.Blitzen SELECT NULL, 'A';
INSERT dbo.Blitzen SELECT 1, NULL;

SELECT COUNT(*), COUNT(1), COUNT(ID), COUNT(Somelala) FROM dbo.Blitzen;
GO

DROP TABLE dbo.Blitzen;
GO
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.