Count (*) vs Count (1) - SQL Server


738

Hanya ingin tahu apakah ada di antara Anda yang menggunakan Count(1)lebih dari Count(*)dan apakah ada perbedaan yang nyata dalam kinerja atau apakah ini hanya kebiasaan lama yang telah dimajukan sejak masa lalu?

Database spesifiknya adalah SQL Server 2005.


7
Tidak tahu tentang SQL Server tetapi di MySQL tidak ada perbedaan. COUNT (kolom) di sisi lain berbeda
Greg

118
Tidak benar. COUNT (SomeColumn) hanya akan mengembalikan jumlah baris yang berisi nilai-nilai non-null untuk SomeColumn. COUNT (*) dan COUNT ('Foo') akan mengembalikan jumlah total baris dalam tabel.
Steve Broberg

1
untuk perincian lebih lanjut, periksa penghitungan pilih
Ali Adravi

4
Wow Steve dan di sini saya berumur 5 tahun di TSQL tanpa mengetahui count (*) vs Count (ColumnName). Terima kasih
Harindaka

Jawaban:


598

Tidak ada perbedaan.

Alasan:

Buku-buku online mengatakan " COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1" adalah ekspresi non-null: jadi itu sama dengan COUNT(*). Pengoptimal mengenalinya apa adanya: sepele.

Sama dengan EXISTS (SELECT * ...atauEXISTS (SELECT 1 ...

Contoh:

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

IO yang sama, rencana yang sama, bekerja

Edit, Agustus 2011

Pertanyaan serupa pada DBA.SE .

Edit, Des 2011

COUNT(*)disebutkan secara khusus dalam ANSI-92 (cari " Scalar expressions 125")

Kasus:

a) Jika COUNT (*) ditentukan, maka hasilnya adalah kardinalitas T.

Artinya, standar ANSI mengenalinya sebagai pendarahan jelas apa yang Anda maksud. COUNT(1)telah dioptimalkan oleh vendor RDBMS karena takhayul ini. Kalau tidak, akan dievaluasi sesuai ANSI

b) Kalau tidak, biarkan TX menjadi tabel satu kolom yang merupakan hasil dari penerapan <ekspresi nilai> untuk setiap baris T dan menghilangkan nilai nol. Jika satu atau lebih nilai nol dihilangkan, maka kondisi penyelesaian dinaikkan: peringatan-


73

Di SQL Server, pernyataan ini menghasilkan paket yang sama.

Bertentangan dengan pendapat umum, di Oracle mereka juga melakukannya.

SYS_GUID() di Oracle adalah fungsi intensif komputasi.

Dalam database pengujian saya, t_evenadalah tabel dengan 1,000,000baris

Kueri ini:

SELECT  COUNT(SYS_GUID())
FROM    t_even

berjalan selama beberapa 48detik, karena fungsi perlu mengevaluasi masing-masing yang SYS_GUID()dikembalikan untuk memastikan itu bukan NULL.

Namun, pertanyaan ini:

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

berjalan selama beberapa 2detik, karena ia bahkan tidak mencoba untuk mengevaluasi SYS_GUID()(meskipun *ada argumen untuk COUNT(*))


itu harus mengevaluasi SYS_GUID()setidaknya (maksud saya, tepatnya) sekali untuk sub-query untuk mengembalikan hasil, kan?
asgs

@gs: mengapa menurut Anda begitu? Bagaimana cara COUNT(*)bergantung pada nilai-nilai SYS_GUID?
Quassnoi

Sekarang Anda bertanya, saya tidak yakin. Saya pikir untuk COUNT(*)menjalankan, perlu tabel, jadi sub-kueri harus bertindak seperti satu. Kalau tidak, saya tidak melihat cara untuk COUNT(*)mengembalikan nilai yang berarti
asgs

1
@ asgs: dengan asumsi Anda tahu apa yang dilakukan mapmetode ini, apakah Anda melihat bagaimana kedua ekspresi ini: t_even.map(() => sys_guid()).lengthdan t_even.lengthakan selalu mengembalikan nilai yang sama? Pengoptimal Oracle cukup pintar untuk melihatnya dan mengoptimalkan mapbagian luar.
Quassnoi

1
@gs tepatnya. Hanya koreksi kecil: lengthtidak terlalu tergantung pada apa koleksi terdiri, hanya pada jumlah elemennya. Jika nomor ini disimpan dalam metadata koleksi (ini bukan kasus untuk Oracle atau kebanyakan RDBMS modern lainnya tetapi demikian halnya dengan mesin penyimpanan MySQL lama, MyISAM), maka COUNT(*)hanya perlu mengambil nilai dari metadata.
Quassnoi

65

Jelas, COUNT(*)dan COUNT(1)akan selalu mengembalikan hasil yang sama. Oleh karena itu, jika salah satu lebih lambat dari yang lain itu akan secara efektif disebabkan oleh bug pengoptimal. Karena kedua formulir ini sangat sering digunakan dalam kueri, tidak masuk akal jika DBMS membiarkan bug semacam itu tetap tidak diperbaiki. Karenanya Anda akan menemukan bahwa kinerja kedua bentuk (mungkin) identik di semua DBMS SQL utama.


Saya tidak akan menganggapnya sebagai bug jika hitungan (1) lebih lambat dari hitungan (*). Jika Anda meminta dbms untuk menghasilkan 1s dan menghitung yang bukan nol, maka ya, itu bermuara pada jumlah catatan, tetapi Anda tidak dapat mengharapkan dbms untuk mendeteksi setiap omong kosong yang Anda tulis dan mengelilinginya untuk Anda.
Thorsten Kettner

1
Yah, pengoptimal dimaksudkan untuk mengoptimalkan dan untuk hitungan hanya ada 2 kasus untuk dipertimbangkan: ekspresi yang mungkin nol, ekspresi yang tidak akan pernah menjadi nol: hitungan (1) termasuk yang terakhir sehingga tidak perlu bagi DBMS untuk "menghasilkan" 1s untuk menjawab pertanyaan. (BTW saya tidak akan pernah menggunakan apa pun kecuali hitungan (*), hanya untuk alasan estetika.)
Tony Andrews

46

Saya bekerja di tim SQL Server dan mudah-mudahan saya bisa mengklarifikasi beberapa poin di utas ini (saya belum pernah melihatnya sebelumnya, jadi saya minta maaf tim teknik belum melakukannya sebelumnya).

Pertama, tidak ada perbedaan semantik antara select count(1) from tablevs select count(*) from table. Mereka mengembalikan hasil yang sama dalam semua kasus (dan itu adalah bug jika tidak). Sebagaimana dicatat dalam jawaban lain, select count(column) from tablesecara semantik berbeda dan tidak selalu memberikan hasil yang sama count(*).

Kedua, berkenaan dengan kinerja, ada dua aspek yang penting dalam SQL Server (dan SQL Azure): kerja waktu kompilasi dan kerja waktu eksekusi. Pekerjaan waktu kompilasi adalah pekerjaan ekstra kecil yang sepele dalam implementasi saat ini. Ada ekspansi * ke semua kolom dalam beberapa kasus diikuti oleh pengurangan kembali ke 1 kolom menjadi output karena bagaimana beberapa operasi internal bekerja dalam pengikatan dan optimalisasi. Saya ragu itu akan muncul dalam tes terukur apa pun, dan kemungkinan akan hilang dalam kebisingan semua hal lain yang terjadi di bawah selimut (seperti statistik otomatis, sesi xevent, overhead kueri toko, pemicu, dll.). Mungkin beberapa ribu instruksi CPU tambahan. Begitu, count (1) melakukan sedikit kerja lebih sedikit selama kompilasi (yang biasanya akan terjadi sekali dan rencana di-cache di beberapa eksekusi berikutnya). Untuk waktu pelaksanaan, dengan asumsi rencana adalah sama, seharusnya tidak ada perbedaan yang terukur. (Salah satu contoh sebelumnya menunjukkan perbedaan - kemungkinan besar karena faktor lain pada mesin jika rencananya sama).

Mengenai bagaimana rencana tersebut berpotensi berbeda. Ini sangat tidak mungkin terjadi, tetapi sangat mungkin dalam arsitektur optimizer saat ini. Pengoptimal SQL Server berfungsi sebagai program pencarian (pikirkan: program komputer bermain catur mencari melalui berbagai alternatif untuk berbagai bagian permintaan dan menghitung alternatif untuk menemukan paket termurah dalam waktu yang wajar). Pencarian ini memiliki beberapa batasan tentang bagaimana ia beroperasi untuk menjaga penyelesaian kompilasi permintaan dalam waktu yang wajar. Untuk kueri di luar yang paling sepele, ada fase pencarian dan mereka berurusan dengan tahapan permintaan berdasarkan seberapa mahal pengoptimal berpikir kueri berpotensi dieksekusi. Ada 3 fase pencarian utama, dan setiap fase dapat menjalankan heuristik yang lebih agresif (mahal) mencoba menemukan rencana yang lebih murah daripada solusi sebelumnya. Pada akhirnya, ada proses pengambilan keputusan di akhir setiap fase yang mencoba menentukan apakah harus mengembalikan rencana yang ditemukan sejauh ini atau harus terus mencari. Proses ini menggunakan total waktu yang diambil sejauh ini vs perkiraan biaya rencana terbaik yang ditemukan sejauh ini. Jadi, pada mesin yang berbeda dengan kecepatan CPU yang berbeda adalah mungkin (walaupun jarang) untuk mendapatkan paket yang berbeda karena penghentian waktu pada fase sebelumnya dengan rencana vs melanjutkan ke fase pencarian berikutnya. Ada juga beberapa skenario serupa yang terkait dengan waktu keluar dari fase terakhir dan berpotensi kehabisan memori pada permintaan yang sangat, sangat mahal yang mengkonsumsi semua memori pada mesin (biasanya tidak masalah pada 64-bit tetapi itu adalah masalah yang lebih besar kembali pada server 32-bit). Pada akhirnya, jika Anda mendapatkan paket yang berbeda, kinerja saat runtime akan berbeda. Saya tidak

Net-net: Silakan gunakan yang mana dari dua yang Anda inginkan karena tidak ada yang penting dalam bentuk praktis. (Ada jauh, faktor-faktor yang jauh lebih besar yang mempengaruhi kinerja dalam SQL di luar topik ini, jujur).

Saya harap ini membantu. Saya memang menulis bab buku tentang cara kerja pengoptimal tapi saya tidak tahu apakah pantas untuk mempostingnya di sini (karena saya mendapatkan sedikit royalti dari itu masih saya percaya). Jadi, alih-alih memposting, saya akan memposting tautan ke ceramah yang saya berikan di SQLBits di Inggris tentang bagaimana pengoptimal bekerja pada level tinggi sehingga Anda dapat melihat berbagai fase utama pencarian dengan sedikit lebih detail jika Anda mau untuk belajar tentang itu. Inilah tautan videonya: https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer


2
Keyakinan saya adalah yang 1juga mengalami ekspansi yang sama. Saya mendasarkan ini pada tes perf di sini stackoverflow.com/questions/1597442/... juga melihat contoh dalam jawaban permintaan menggunakan 1gagal secara tak terduga ketika izin tingkat kolom sedang dimainkan
Martin Smith

21

Dalam Standar SQL-92, COUNT(*)secara khusus berarti "kardinalitas ekspresi tabel" (bisa berupa tabel dasar, `LIHAT, tabel turunan, CTE, dll).

Saya kira idenya COUNT(*)mudah diurai. Menggunakan ekspresi lain membutuhkan parser untuk memastikannya tidak mereferensikan kolom apa pun (di COUNT('a')mana aliteral dan di COUNT(a)mana akolom dapat menghasilkan hasil yang berbeda).

Dalam nada yang sama, COUNT(*)dapat dengan mudah dipilih oleh seorang pengkode manusia yang akrab dengan Standar SQL, keterampilan yang berguna ketika bekerja dengan lebih dari satu penawaran SQL vendor.

Juga, dalam kasus khusus SELECT COUNT(*) FROM MyPersistedTable;, pemikirannya adalah DBMS cenderung memiliki statistik kardinalitas tabel.

Karena itu, karena COUNT(1)dan COUNT(*)secara semantik setara, saya gunakan COUNT(*).


1
Teks SQL-92 ditautkan dari jawaban saya di DBA.SE: dba.stackexchange.com/questions/2511/…
gbn


12

Saya berharap pengoptimal memastikan tidak ada perbedaan nyata di luar kasus tepi aneh.

Seperti apa pun, satu-satunya cara nyata untuk mengetahui adalah dengan mengukur kasus spesifik Anda.

Yang mengatakan, saya selalu digunakan COUNT(*).


Per jawaban yang diterima, ini tidak benar untuk MS SQL - sebenarnya tidak ada perbedaan antara keduanya.
David Manheim

10

Ketika pertanyaan ini muncul berulang-ulang, inilah satu jawaban lagi. Saya berharap dapat menambahkan sesuatu untuk pemula yang bertanya-tanya tentang "praktik terbaik" di sini.

SELECT COUNT(*) FROM something menghitung catatan yang merupakan tugas yang mudah.

SELECT COUNT(1) FROM something mengambil 1 per catatan dan menghitung 1 yang bukan nol, yang pada dasarnya menghitung catatan, hanya lebih rumit.

Setelah mengatakan ini: Pemberitahuan dbms yang baik bahwa pernyataan kedua akan menghasilkan jumlah yang sama dengan pernyataan pertama dan menerjemahkan ulang sesuai dengan itu, karena tidak melakukan pekerjaan yang tidak perlu. Jadi biasanya kedua pernyataan akan menghasilkan rencana eksekusi yang sama dan mengambil jumlah waktu yang sama.

Namun dari sudut keterbacaan Anda harus menggunakan pernyataan pertama. Anda ingin menghitung catatan, jadi hitung catatan, bukan ekspresi. Gunakan COUNT (ekspresi) hanya ketika Anda ingin menghitung kejadian sesuatu yang bukan nol.


8

Saya menjalankan tes cepat pada SQL Server 2012 pada kotak hyper-v RAM 8 GB. Anda dapat melihat hasilnya sendiri. Saya tidak menjalankan aplikasi berjendela lain selain dari SQL Server Management Studio saat menjalankan tes ini.

Skema tabel saya:

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

Total jumlah catatan dalam Employeetabel: 178090131 (~ 178 juta baris)

Pertanyaan Pertama:

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

Hasil Kueri Pertama:

 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.

Pertanyaan Kedua:

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

Hasil Kueri Kedua:

 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.

Anda dapat melihat ada perbedaan 83 (= 70265 - 70182) milidetik yang dapat dengan mudah dikaitkan dengan kondisi sistem yang tepat pada saat kueri dijalankan. Saya juga menjalankan satu kali, jadi perbedaan ini akan menjadi lebih akurat jika saya melakukan beberapa kali dan rata-rata. Jika untuk kumpulan data yang begitu besar perbedaannya datang kurang dari 100 milidetik, maka kita dapat dengan mudah menyimpulkan bahwa dua kueri tidak memiliki perbedaan kinerja yang ditunjukkan oleh SQL Server Engine.

Catatan : RAM mencapai penggunaan hampir 100% di kedua proses. Saya memulai kembali layanan SQL Server sebelum memulai kedua proses.


7
SET STATISTICS TIME ON

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

Waktu Eksekusi SQL Server:
Waktu CPU = 31 ms, waktu yang berlalu = 36 ms.

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

Waktu Eksekusi SQL Server:
Waktu CPU = 46 ms, waktu yang berlalu = 37 ms.

Saya telah menjalankan ini ratusan kali, membersihkan cache setiap waktu .. Hasilnya bervariasi dari waktu ke waktu karena beban server bervariasi, tetapi hampir selalu count(*)memiliki waktu cpu yang lebih tinggi.


14
Saya tidak bisa mereproduksi ini. count(*)dan count(1)hasil kembali dalam beberapa ms satu sama lain, bahkan ketika menghitung tabel dengan 4,5 juta baris, dalam contoh SQL 2008 saya.
Jeff Atwood

2
Terkadang, dalam beberapa sistem, pernyataan yang dijalankan terlebih dahulu selalu berjalan lebih cepat ... apakah Anda secara acak urutan di mana mereka dijalankan?
JosephDoggie

@JosephDoggie seseorang harus selalu memulai kembali layanan SQL Server sebelum menjalankan setiap permintaan sambil mengambil pengukuran / statistik tersebut. Ketika Anda baru saja memulai layanan SQL Server maka setiap proses menjadi benar-benar independen dan menerima urutan permintaan seharusnya tidak menjadi masalah. Di sisi lain, jika Anda tidak me-restart layanan SQL Server dan mesin melakukan semacam caching dari rencana eksekusi maka permintaan yang dijalankan nanti harus berjalan lebih cepat bukan yang pertama.
RBT

Waktu eksekusi perlu melihat paket permintaan yang tepat saat melakukan perbandingan. Jika mereka berbeda (katakanlah, agregat hash vs. sort + aliran agregat), maka hasilnya tidak sebanding. Jadi, saya mendesak agar hati-hati menarik kesimpulan di sini tanpa lebih banyak data.
Conor Cunningham MSFT

3

Ada sebuah artikel yang menunjukkan bahwa COUNT(1)pada Oracle adalah hanya sebuah alias untuk COUNT(*), dengan bukti tentang itu.

Saya akan mengutip beberapa bagian:

Ada bagian dari perangkat lunak basis data yang disebut "Pengoptimal", yang didefinisikan dalam dokumentasi resmi sebagai "perangkat lunak basis data bawaan yang menentukan cara paling efisien untuk menjalankan pernyataan SQL".

Salah satu komponen dari pengoptimal disebut "transformator", yang perannya adalah menentukan apakah menguntungkan untuk menulis ulang pernyataan SQL asli menjadi pernyataan SQL yang setara secara semantik yang bisa lebih efisien.

Apakah Anda ingin melihat apa yang dilakukan pengoptimal ketika Anda menulis kueri menggunakan COUNT (1)?

Dengan pengguna dengan ALTER SESSIONhak istimewa, Anda dapat menempatkan sebuah tracefile_identifier, memungkinkan tracing optimizer dan menjalankan COUNT(1)pilih, seperti: SELECT /* test-1 */ COUNT(1) FROM employees;.

Setelah itu, Anda perlu melokalkan file jejak, apa yang bisa dilakukan SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace';. Kemudian pada file, Anda akan menemukan:

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

Seperti yang Anda lihat, itu hanya alias untuk COUNT(*).

Komentar penting lainnya: Oracle COUNT(*)benar-benar lebih cepat dua dekade yang lalu , sebelum Oracle 7.3:

Hitung (1) telah ditulis ulang dalam hitung (*) sejak 7.3 karena Oracle suka dengan pernyataan mitos Auto-tune. Dalam Oracle7 sebelumnya, oracle harus mengevaluasi (1) untuk setiap baris, sebagai fungsi, sebelum DETERMINISTIC dan NON-DETERMINISTIC ada.

Jadi dua dekade lalu, hitungan (*) lebih cepat

Untuk database lain sebagai Sql Server, itu harus diteliti secara individual untuk masing-masing.

Saya tahu bahwa pertanyaan ini khusus untuk Sql Server, tetapi pertanyaan lain pada SO tentang subjek yang sama, tanpa menyebutkan database, ditutup dan ditandai sebagai duplikat dari jawaban ini.


1

Dalam semua RDBMS, dua cara penghitungan adalah setara dalam hal hasil apa yang mereka hasilkan. Mengenai kinerja, saya belum mengamati perbedaan kinerja dalam SQL Server, tetapi mungkin perlu menunjukkan bahwa beberapa RDBMS, misalnya PostgreSQL 11, memiliki implementasi yang kurang optimal karena COUNT(1)mereka memeriksa nullability ekspresi argumen seperti yang dapat dilihat pada posting ini .

Saya telah menemukan perbedaan kinerja 10% untuk baris 1M saat menjalankan:

-- Faster
SELECT COUNT(*) FROM t;

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

0

COUNT (1) tidak jauh berbeda dari COUNT (*), jika sama sekali. Mengenai pertanyaan MENGHITUNG KOLOM NULLable, ini bisa langsung untuk mendemonstrasikan perbedaan antara COUNT (*) dan COUNT (<beberapa col>) -

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.