Sql Server setara dengan fungsi agregat COUNTIF

164

Saya sedang membangun kueri dengan GROUP BYklausa yang membutuhkan kemampuan untuk menghitung catatan hanya berdasarkan kondisi tertentu (misalnya menghitung hanya catatan di mana nilai kolom tertentu sama dengan 1).

SELECT  UID, 
        COUNT(UID) AS TotalRecords, 
        SUM(ContractDollars) AS ContractDollars,
        (COUNTIF(MyColumn, 1) / COUNT(UID) * 100) -- Get the average of all records that are 1
FROM    dbo.AD_CurrentView
GROUP BY UID
HAVING  SUM(ContractDollars) >= 500000

The COUNTIF()garis jelas gagal karena tidak ada fungsi SQL asli disebut COUNTIF, tapi ide di sini adalah untuk menentukan persentase semua baris yang memiliki nilai '1' untuk MyColumn.

Adakah pemikiran tentang bagaimana menerapkan ini dengan benar di lingkungan MS SQL 2005?

senfo
sumber

Jawaban:

339

Anda dapat menggunakan SUM(tidak COUNT!) Yang dikombinasikan dengan CASEpernyataan, seperti ini:

SELECT SUM(CASE WHEN myColumn=1 THEN 1 ELSE 0 END)
FROM AD_CurrentView

Catatan: dalam pengujian saya sendiri NULL, bukan masalah, meskipun ini bisa bergantung pada lingkungan. Anda dapat menangani nulls seperti:

SELECT SUM(CASE WHEN ISNULL(myColumn,0)=1 THEN 1 ELSE 0 END)
FROM AD_CurrentView
JoshBerke
sumber
3
(Saya tahu OP bertanya tentang MS SQL, tetapi hanya sebuah komentar kecil untuk pengguna SQLite melakukan hal yang sama) SQLite tidak memiliki ISNULL, sebaliknya Anda dapat melakukannya CASE WHEN myColumn IS NULL, atau menggunakan ifnull( stackoverflow.com/a/799406/1861346 )
Matt
54

Saya biasanya melakukan apa yang direkomendasikan Josh, tetapi melakukan brainstorming dan menguji alternatif yang agak tipu yang ingin saya bagikan.

Anda dapat memanfaatkan fakta bahwa COUNT (ColumnName) tidak menghitung NULL, dan menggunakan sesuatu seperti ini:

SELECT COUNT(NULLIF(0, myColumn))
FROM AD_CurrentView

NULLIF - mengembalikan NULL jika kedua nilai yang diteruskan sama.

Keuntungan: Mengungkapkan maksud Anda ke COUNT baris alih-alih memiliki notasi SUM (). Kerugian: Tidak jelas bagaimana cara kerjanya ("sihir" biasanya buruk).

Chris Shaffer
sumber
2
Solusi ini dapat memberikan jawaban yang berbeda dari jumlah ketika sebuah grup hanya berisi nol yang dihasilkannya 1 bukannya 0.
KimvdLinde
Posting lama, tapi terima kasih ini membantu. Saya diperpanjang sihir dan mendapat sekitar "hanya nulls" masalah dengan menambahkan ISNULLsebagai berikut: SELECT COUNT(NULLIF(0, ISNULL(myColumn, 0))). Tunggu, itu hanya terlihat jelek ...
pcdev
1
Akan sempurna jika ada fungsi
NULLIFNOT
21

Saya akan menggunakan sintaks ini. Ini mencapai hal yang sama seperti saran Josh dan Chris, tetapi dengan keuntungan itu sesuai dengan ANSI dan tidak terikat dengan vendor database tertentu.

select count(case when myColumn = 1 then 1 else null end)
from   AD_CurrentView
asgeo1
sumber
2
Jawaban Chris adalah sesuai dengan Stndard SQL (petunjuk: NULLIFtermasuk Standard SQL-92). Jawabannya Josh dapat dengan mudah diubah menjadi Standar SQL dengan mengganti isnulldengan COALESCE.
onedaywhen
Saya sebenarnya paling suka jawaban ini, karena mendapat ide "menghitung baris" yang ditunjukkan Chris, tetapi lebih dapat diperluas, karena Anda dapat menggunakan operator perbandingan; bukan hanya =. Saya menggunakannya untuk "menghitung jumlah tanggapan> = 2".
Kristen Hammack
3

Menambahkan ke jawaban Josh,

SELECT COUNT(CASE WHEN myColumn=1 THEN AD_CurrentView.PrimaryKeyColumn ELSE NULL END)
FROM AD_CurrentView

Bekerja dengan baik untuk saya (dalam SQL Server 2012) tanpa mengubah 'hitung' menjadi 'jumlah' dan logika yang sama portabel untuk 'agregat kondisional' lainnya. Misalnya, penjumlahan berdasarkan suatu kondisi:

SELECT SUM(CASE WHEN myColumn=1 THEN AD_CurrentView.NumberColumn ELSE 0 END)
FROM AD_CurrentView
Sturgus
sumber
2

Bagaimana tentang

SELECT id, COUNT(IF status=42 THEN 1 ENDIF) AS cnt
FROM table
GROUP BY table

Lebih pendek dari CASE:)

Berfungsi karena COUNT()tidak menghitung nilai nol, dan IF/ CASEmengembalikan nol ketika kondisi tidak terpenuhi dan tidak ada ELSE.

Saya pikir ini lebih baik daripada menggunakan SUM().

maf-lunak
sumber
1

Tidak spesifik produk, tetapi standar SQL menyediakan

SELECT COUNT() FILTER WHERE <condition-1>, COUNT() FILTER WHERE <condition-2>, ... FROM ...

untuk tujuan ini. Atau sesuatu yang sangat mirip, saya tidak tahu dari atas topiku.

Dan tentu saja vendor akan lebih memilih untuk tetap dengan solusi eksklusif mereka.

Erwin Smout
sumber
1
Saya belum pernah mendengar ini sebelumnya, jadi saya mencarinya. Menurut modern-sql.com/feature/filter , satu-satunya DBMS utama yang benar-benar menawarkan FILTERklausa adalah PostgreSQL, tetapi ditiru oleh CASEsemuanya.
Kristen Hammack
1

Kenapa tidak seperti ini?

SELECT count(1)
FROM AD_CurrentView
WHERE myColumn=1
Michal
sumber
1
Karena dia membutuhkan lebih dari sekedar hitungan. Dia mencoba mendapatkan jumlah baris bagian dari grup, dan kemudian agregat dari seluruh grup, yang tidak dapat Anda lakukan dengan WHERE.
Kristen Hammack
1

Saya harus menggunakan COUNTIF () dalam kasus saya sebagai bagian dari kolom SELECT saya DAN untuk meniru% dari berapa kali setiap item muncul di hasil saya.

Jadi saya menggunakan ini ...

SELECT COL1, COL2, ... ETC
       (1 / SELECT a.vcount 
            FROM (SELECT vm2.visit_id, count(*) AS vcount 
                  FROM dbo.visitmanifests AS vm2 
                  WHERE vm2.inactive = 0 AND vm2.visit_id = vm.Visit_ID 
                  GROUP BY vm2.visit_id) AS a)) AS [No of Visits],
       COL xyz
FROM etc etc

Tentu saja Anda perlu memformat hasilnya sesuai dengan kebutuhan tampilan Anda.

Fandango68
sumber
-2
SELECT COALESCE(IF(myColumn = 1,COUNT(DISTINCT NumberColumn),NULL),0) column1,
COALESCE(CASE WHEN myColumn = 1 THEN COUNT(DISTINCT NumberColumn) ELSE NULL END,0) AS column2
FROM AD_CurrentView
Andres Sarria
sumber