Anda kebanyakan sudah menjawab pertanyaan itu sendiri. Saya punya beberapa bagian untuk ditambahkan:
Di PostgreSQL (dan RDBMS lainnya yang mendukung boolean
tipe), Anda dapat menggunakan boolean
hasil tes secara langsung. Keluarkan ke integer
dan SUM()
:
SUM((amount > 100)::int))
Atau gunakan dalam NULLIF()
ekspresi dan COUNT()
:
COUNT(NULLIF(amount > 100, FALSE))
Atau dengan yang sederhana OR NULL
:
COUNT(amount > 100 OR NULL)
Atau berbagai ekspresi lainnya. Performanya hampir identik . COUNT()
biasanya sangat sedikit lebih cepat daripada SUM()
. Berbeda SUM()
dan seperti Paul sudah berkomentar , COUNT()
tidak pernah kembali NULL
, yang mungkin nyaman. Terkait:
Sejak Postgres 9.4 ada juga FILTER
klausa . Detail:
Lebih cepat dari semua hal di atas sekitar 5 - 10%:
COUNT(*) FILTER (WHERE amount > 100)
Jika kueri sesederhana kasus pengujian Anda, dengan hanya satu hitungan dan tidak ada yang lain, Anda dapat menulis ulang:
SELECT count(*) FROM tbl WHERE amount > 100;
Yang merupakan raja sejati kinerja, bahkan tanpa indeks.
Dengan indeks yang berlaku, indeks dapat lebih cepat berdasarkan pesanan, terutama dengan pemindaian hanya indeks.
Tingkatan yang dicapai
Postgres 10
Saya menjalankan serangkaian tes baru untuk Postgres 10, termasuk FILTER
klausa agregat dan menunjukkan peran indeks untuk jumlah kecil dan besar.
Pengaturan sederhana:
CREATE TABLE tbl (
tbl_id int
, amount int NOT NULL
);
INSERT INTO tbl
SELECT g, (random() * 150)::int
FROM generate_series (1, 1000000) g;
-- only relevant for the last test
CREATE INDEX ON tbl (amount);
Waktu yang sebenarnya bervariasi sedikit karena kebisingan latar belakang dan spesifik dari tempat tidur uji. Menampilkan waktu terbaik khas dari serangkaian tes yang lebih besar. Dua kasus ini harus menangkap esensi:
Tes 1 penghitungan ~ 1% dari semua baris
SELECT COUNT(NULLIF(amount > 148, FALSE)) FROM tbl; -- 140 ms
SELECT SUM((amount > 148)::int) FROM tbl; -- 136 ms
SELECT SUM(CASE WHEN amount > 148 THEN 1 ELSE 0 END) FROM tbl; -- 133 ms
SELECT COUNT(CASE WHEN amount > 148 THEN 1 END) FROM tbl; -- 130 ms
SELECT COUNT((amount > 148) OR NULL) FROM tbl; -- 130 ms
SELECT COUNT(*) FILTER (WHERE amount > 148) FROM tbl; -- 118 ms -- !
SELECT count(*) FROM tbl WHERE amount > 148; -- without index -- 75 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 148; -- with index -- 1.4 ms -- !!!
db <> biola di sini
Tes 2 menghitung ~ 33% dari semua baris
SELECT COUNT(NULLIF(amount > 100, FALSE)) FROM tbl; -- 140 ms
SELECT SUM((amount > 100)::int) FROM tbl; -- 138 ms
SELECT SUM(CASE WHEN amount > 100 THEN 1 ELSE 0 END) FROM tbl; -- 139 ms
SELECT COUNT(CASE WHEN amount > 100 THEN 1 END) FROM tbl; -- 138 ms
SELECT COUNT(amount > 100 OR NULL) FROM tbl; -- 137 ms
SELECT COUNT(*) FILTER (WHERE amount > 100) FROM tbl; -- 132 ms -- !
SELECT count(*) FROM tbl WHERE amount > 100; -- without index -- 102 ms -- !!
SELECT count(*) FROM tbl WHERE amount > 100; -- with index -- 55 ms -- !!!
db <> biola di sini
Tes terakhir di setiap set menggunakan pemindaian hanya indeks , itulah sebabnya itu membantu untuk menghitung sepertiga dari semua baris. Pemindaian indeks biasa atau indeks bitmap tidak dapat bersaing dengan pemindaian sekuensial ketika melibatkan sekitar 5% atau lebih dari semua baris.
Tes lama untuk Postgres 9.1
Untuk memverifikasi saya menjalankan tes cepat dengan EXPLAIN ANALYZE
pada tabel kehidupan nyata di PostgreSQL 9.1.6.
74208 dari 184568 baris memenuhi syarat dengan kondisi tersebut kat_id > 50
. Semua pertanyaan mengembalikan hasil yang sama. Saya berlari masing-masing seperti 10 kali secara bergantian untuk mengecualikan efek caching dan menambahkan hasil terbaik sebagai catatan:
SELECT SUM((kat_id > 50)::int) FROM log_kat; -- 438 ms
SELECT COUNT(NULLIF(kat_id > 50, FALSE)) FROM log_kat; -- 437 ms
SELECT COUNT(CASE WHEN kat_id > 50 THEN 1 END) FROM log_kat; -- 437 ms
SELECT COUNT((kat_id > 50) OR NULL) FROM log_kat; -- 436 ms
SELECT SUM(CASE WHEN kat_id > 50 THEN 1 ELSE 0 END) FROM log_kat; -- 432 ms
Hampir tidak ada perbedaan nyata dalam kinerja.
FILTER
daripada dengan ekspresi di atas (pengujian dengan hal 9.5). Apakah Anda mendapatkan yang sama? (WHERE
Masih raja kinerja - jika memungkinkan).FILTER
solusi adalah biasanya lebih cepat dalam tes saya.Ini adalah pengujian saya pada SQL Server 2012 RTM.
Melihat masing-masing berlari dan batch secara terpisah
Hasil setelah menjalankan 5 kali (dan berulang) cukup tidak meyakinkan.
Ini menunjukkan bahwa ada jauh lebih variabilitas dalam kondisi berjalan daripada ada perbedaan antara implementasi, bila diukur dengan granularity timer SQL Server. Versi mana pun dapat muncul di atas, dan varians maksimum yang pernah saya dapatkan adalah 2,5%.
Namun, mengambil pendekatan yang berbeda:
StmtText (SUM)
StmtText (COUNT)
Dari bacaan saya, akan terlihat bahwa versi SUM melakukan sedikit lebih banyak. Itu melakukan COUNT selain SUM. Karena itu,
COUNT(*)
berbeda dan harus lebih cepat daripadaCOUNT([Expr1004])
(melewatkan NULLs, lebih banyak logika). Pengoptimal yang masuk akal akan menyadari bahwa[Expr1004]
diSUM([Expr1004])
dalam versi SUM adalah tipe "int" dan karenanya menggunakan register integer.Bagaimanapun, sementara saya masih percaya
COUNT
versi akan lebih cepat di sebagian besar RDBMS, kesimpulan saya dari pengujian adalah bahwa saya akan pergi denganSUM(.. 1.. 0..)
di masa depan, setidaknya untuk SQL Server tanpa alasan lain selain PERINGATAN ANSI yang dimunculkan ketika menggunakanCOUNT
.sumber
Dalam pengalaman saya Membuat jejak, untuk kedua metode dalam Permintaan sekitar 10.000.000 Saya Melihat Hitung (*) menggunakan sekitar dua kali CPU dan berjalan sedikit lebih cepat. tapi Pertanyaan saya tanpa filter.
Menghitung(*)
Jumlah (1)
sumber