Bagaimana cara mendapatkan beberapa hitungan dengan satu query SQL?

316

Saya bertanya-tanya bagaimana cara menulis kueri ini.

Saya tahu sintaks yang sebenarnya ini palsu, tetapi ini akan membantu Anda memahami apa yang saya inginkan. Saya membutuhkannya dalam format ini, karena ini adalah bagian dari permintaan yang jauh lebih besar.

SELECT distributor_id, 
COUNT(*) AS TOTAL, 
COUNT(*) WHERE level = 'exec', 
COUNT(*) WHERE level = 'personal'

Saya perlu ini semua dikembalikan dalam satu permintaan.

Selain itu, harus dalam satu baris, sehingga yang berikut ini tidak akan berfungsi:

'SELECT distributor_id, COUNT(*)
GROUP BY distributor_id'
Crobzilla
sumber
1
Apakah permintaan Anda ini berfungsi dengan baik ?? SELECT distributor_id, COUNT(*) AS TOTAL, COUNT(*) WHERE level = 'exec', COUNT(*) WHERE level = 'personal'
Pratik

Jawaban:

690

Anda bisa menggunakan CASEpernyataan dengan fungsi agregat. Ini pada dasarnya sama dengan PIVOTfungsi pada beberapa RDBMS:

SELECT distributor_id,
    count(*) AS total,
    sum(case when level = 'exec' then 1 else 0 end) AS ExecCount,
    sum(case when level = 'personal' then 1 else 0 end) AS PersonalCount
FROM yourtable
GROUP BY distributor_id
Taryn
sumber
55
Fantastis, ini luar biasa. Jawaban yang bagus Hanya catatan untuk orang-orang yang tersandung di sini. Hitungan akan menghitung semua baris, jumlah akan melakukan hal yang sama dengan perhitungan ketika digunakan dengan pernyataan kasus.
John Ballinger
1
Solusi brilian! Mungkin perlu dicatat bahwa metode ini berfungsi dengan baik jika Anda menggabungkan banyak tabel bersama dalam satu permintaan, karena menggunakan sub-kueri bisa menjadi sangat berantakan dalam hal itu.
Darren Crabb
7
Terima kasih atas solusi yang sangat elegan ini. Btw, ini juga berfungsi dengan TSQL.
Annie Lagang
6
Mengapa ini mungkin bukan jawaban terbaik: selalu pindai tabel penuh. Pertimbangkan gabungan sub-kueri penghitungan, atau jumlah bersarang dalam suatu pilihan. Namun tanpa ada indeks, ini mungkin yang terbaik karena Anda telah menjamin hanya satu pemindaian tabel vs banyak. Lihat jawaban dari @KevinBalmforth
YoYo
1
@ JohnBallinger, 'Hitung akan menghitung semua baris' - COUNTakan dihitung distributor_idbijak. tidak semua baris tabel, kan?
Istiaque Ahmed
88

Salah satu cara yang pasti berhasil

SELECT a.distributor_id,
    (SELECT COUNT(*) FROM myTable WHERE level='personal' and distributor_id = a.distributor_id) as PersonalCount,
    (SELECT COUNT(*) FROM myTable WHERE level='exec' and distributor_id = a.distributor_id) as ExecCount,
    (SELECT COUNT(*) FROM myTable WHERE distributor_id = a.distributor_id) as TotalCount
FROM (SELECT DISTINCT distributor_id FROM myTable) a ;

EDIT:
Lihat uraian kinerja @ KevinBalmforth untuk alasan mengapa Anda mungkin tidak ingin menggunakan metode ini dan sebagai gantinya harus memilih jawaban @ Taryn. Saya meninggalkan ini sehingga orang dapat memahami pilihan mereka.

Bukan saya
sumber
2
Ini membantu saya menyelesaikan cara melakukan beberapa penghitungan dan menghasilkannya dalam satu pernyataan SELECT dengan setiap penghitungan menjadi sebuah kolom. Bekerja dengan baik - terima kasih!
Markus
2
Saya dapat menggunakan apa yang Anda berikan di sini, dalam proyek saya. Sekarang semuanya dalam satu kueri, bukan beberapa kueri. Halaman dimuat dalam waktu kurang dari satu detik, dibandingkan dengan 5-8 detik dengan beberapa kueri. Suka. Terima kasih, Notme.
Wayne Barron
1
Ini mungkin bekerja dengan baik jika masing-masing sub kueri benar-benar mengenai indeks. Jika tidak, maka sum(case...)solusi harus dipertimbangkan.
YoYo
1
Perhatikan bahwa sebagai alternatif untuk berbeda, seperti yang telah saya lakukan koreksi, Anda juga dapat / lebih baik menggunakan group bydengan manfaat mengganti seluruh kueri bersarang dengan sederhana count(*)seperti @Mihai menunjukkan - dengan penyederhanaan sintaksis hanya MySQL saja.
YoYo
43
SELECT 
    distributor_id, 
    COUNT(*) AS TOTAL, 
    COUNT(IF(level='exec',1,null)),
    COUNT(IF(level='personal',1,null))
FROM sometable;

COUNThanya menghitung non nullnilai dan DECODEakan mengembalikan nilai bukan nol 1hanya jika kondisi Anda terpenuhi.

Majid Laissi
sumber
yang distributor_idakan ditampilkan kueri? Ini menunjukkan total 1 baris.
Istiaque Ahmed
OP memiliki grup pada kolom yang dihilangkan dalam jawaban saya.
Majid Laissi
Anda menyelamatkan hidup saya, semua aswers lainnya mengembalikan beberapa baris di MySQL. Terima kasih banyak
Abner
1
@Abner senang ini masih membantu setelah 8 tahun :)
Majid Laissi
@ MadidLaissi ya, mengubah waktu permintaan saya dari satu menit menjadi kurang dari satu detik. :)
Abner
25

Membangun jawaban diposting lainnya.

Kedua hal ini akan menghasilkan nilai yang benar:

select distributor_id,
    count(*) total,
    sum(case when level = 'exec' then 1 else 0 end) ExecCount,
    sum(case when level = 'personal' then 1 else 0 end) PersonalCount
from yourtable
group by distributor_id

SELECT a.distributor_id,
          (SELECT COUNT(*) FROM myTable WHERE level='personal' and distributor_id = a.distributor_id) as PersonalCount,
          (SELECT COUNT(*) FROM myTable WHERE level='exec' and distributor_id = a.distributor_id) as ExecCount,
          (SELECT COUNT(*) FROM myTable WHERE distributor_id = a.distributor_id) as TotalCount
       FROM myTable a ; 

Namun, kinerjanya sangat berbeda, yang jelas akan lebih relevan ketika jumlah data bertambah.

Saya menemukan bahwa, dengan asumsi tidak ada indeks yang didefinisikan pada tabel, kueri yang menggunakan SUM akan melakukan pemindaian tabel tunggal, sedangkan kueri dengan COUNT akan melakukan beberapa pemindaian tabel.

Sebagai contoh, jalankan skrip berikut:

IF OBJECT_ID (N't1', N'U') IS NOT NULL 
drop table t1

create table t1 (f1 int)


    insert into t1 values (1) 
    insert into t1 values (1) 
    insert into t1 values (2)
    insert into t1 values (2)
    insert into t1 values (2)
    insert into t1 values (3)
    insert into t1 values (3)
    insert into t1 values (3)
    insert into t1 values (3)
    insert into t1 values (4)
    insert into t1 values (4)
    insert into t1 values (4)
    insert into t1 values (4)
    insert into t1 values (4)


SELECT SUM(CASE WHEN f1 = 1 THEN 1 else 0 end),
SUM(CASE WHEN f1 = 2 THEN 1 else 0 end),
SUM(CASE WHEN f1 = 3 THEN 1 else 0 end),
SUM(CASE WHEN f1 = 4 THEN 1 else 0 end)
from t1

SELECT 
(select COUNT(*) from t1 where f1 = 1),
(select COUNT(*) from t1 where f1 = 2),
(select COUNT(*) from t1 where f1 = 3),
(select COUNT(*) from t1 where f1 = 4)

Sorot 2 pernyataan SELECT dan klik ikon Display Estimated Execution Plan. Anda akan melihat bahwa pernyataan pertama akan melakukan satu pemindaian tabel dan yang kedua akan 4. 4. Jelas satu pemindaian tabel lebih baik dari 4.

Menambahkan indeks berkerumun juga menarik. Misalnya

Create clustered index t1f1 on t1(f1);
Update Statistics t1;

SELECT pertama di atas akan melakukan satu Pemindaian Indeks Clustered. SELECT kedua akan melakukan 4 Pencarian Indeks Clustered, tetapi mereka masih lebih mahal daripada Scan Index Clustered tunggal. Saya mencoba hal yang sama di atas meja dengan 8 juta baris dan SELECT kedua masih jauh lebih mahal.

Kevin Balmforth
sumber
23

Untuk MySQL, ini dapat disingkat menjadi:

SELECT distributor_id,
    COUNT(*) total,
    SUM(level = 'exec') ExecCount,
    SUM(level = 'personal') PersonalCount
FROM yourtable
GROUP BY distributor_id
Mihai
sumber
1
apakah "grup oleh distributor_id" "benar-benar diperlukan dalam kueri ini? Dapat juga berfungsi tanpa itu
user1451111
2
@ user1451111 pertanyaan asli mendapatkannya jadi jawabannya tergantung pada pertanyaan itu sendiri
Al-Mothafar
11

Nah, jika Anda harus memiliki semuanya dalam satu permintaan, Anda bisa melakukan penyatuan:

SELECT distributor_id, COUNT() FROM ... UNION
SELECT COUNT() AS EXEC_COUNT FROM ... WHERE level = 'exec' UNION
SELECT COUNT(*) AS PERSONAL_COUNT FROM ... WHERE level = 'personal';

Atau, jika Anda dapat melakukannya setelah memproses:

SELECT distributor_id, COUNT(*) FROM ... GROUP BY level;

Anda akan mendapatkan hitungan untuk setiap level dan perlu menjumlahkan semuanya untuk mendapatkan total.

CrazyCasta
sumber
Ditemukan UNIONsangat membantu saat membuat laporan yang berisi banyak contoh COUNT(*)fungsi.
James O
Hasilnya menunjukkan #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ') FROM distributors UNION SELECT COUNT() AS EXEC_COUNT FROM distributors WHERE ' at line 1.
Istiaque Ahmed
jumlah kolom yang dikembalikan dari semua kueri, di mana UNION diterapkan, harus sama. @IstiaqueAhmed mungkin itulah alasan di balik kesalahan Anda.
user1451111
Catatan untuk siapa pun yang menemukan jawaban ini di masa depan. Teknik 'After Processing' yang dijelaskan di sini dapat menyebabkan masalah ketika beberapa nilai dalam kolom 'level' adalah NULL. Dalam hal itu jumlah semua sub-hitungan tidak akan sama dengan jumlah baris total.
user1451111
6

Saya melakukan sesuatu seperti ini di mana saya hanya memberi setiap tabel nama string untuk mengidentifikasinya di kolom A, dan hitungan untuk kolom. Lalu aku menyatukan mereka semua sehingga mereka menumpuk. Hasilnya cukup menurut saya - tidak yakin seberapa efisien itu dibandingkan dengan opsi lain tetapi itu memberi saya apa yang saya butuhkan.

select 'table1', count (*) from table1
union select 'table2', count (*) from table2
union select 'table3', count (*) from table3
union select 'table4', count (*) from table4
union select 'table5', count (*) from table5
union select 'table6', count (*) from table6
union select 'table7', count (*) from table7;

Hasil:

-------------------
| String  | Count |
-------------------
| table1  | 123   |
| table2  | 234   |
| table3  | 345   |
| table4  | 456   |
| table5  | 567   |
-------------------
Frantumn
sumber
1
a query that I created makes ...- di mana permintaan itu?
Istiaque Ahmed
2
cara menambahkan tempat caluse ke semua tabel
3

Berdasarkan tanggapan Bluefeet yang diterima dengan nuansa tambahan menggunakan OVER():

SELECT distributor_id,
    COUNT(*) total,
    SUM(case when level = 'exec' then 1 else 0 end) OVER() ExecCount,
    SUM(case when level = 'personal' then 1 else 0 end) OVER () PersonalCount
FROM yourtable
GROUP BY distributor_id

Menggunakan OVER()apa pun di () akan memberi Anda jumlah total untuk seluruh dataset.

mentor
sumber
1

Saya pikir ini juga bisa bekerja untuk Anda select count(*) as anc,(select count(*) from Patient where sex='F')as patientF,(select count(*) from Patient where sex='M') as patientM from anc

dan juga Anda dapat memilih dan menghitung tabel terkait seperti ini select count(*) as anc,(select count(*) from Patient where Patient.Id=anc.PatientId)as patientF,(select count(*) from Patient where sex='M') as patientM from anc

Sinte
sumber