Làm thế nào để có được nhiều số đếm với một truy vấn SQL?


316

Tôi đang tự hỏi làm thế nào để viết truy vấn này.

Tôi biết cú pháp thực tế này là không có thật, nhưng nó sẽ giúp bạn hiểu những gì tôi muốn. Tôi cần nó ở định dạng này, bởi vì nó là một phần của truy vấn lớn hơn nhiều.

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

Tôi cần tất cả điều này được trả lại trong một truy vấn.

Ngoài ra, nó cần phải nằm trong một hàng, vì vậy những điều sau đây sẽ không hoạt động:

'SELECT distributor_id, COUNT(*)
GROUP BY distributor_id'

1
Truy vấn này của bạn có hoạt động đúng không ?? SELECT distributor_id, COUNT(*) AS TOTAL, COUNT(*) WHERE level = 'exec', COUNT(*) WHERE level = 'personal'
Pratik

Câu trả lời:


690

Bạn có thể sử dụng một CASEcâu lệnh với hàm tổng hợp. Điều này về cơ bản giống như một PIVOThàm trong một số 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

55
Tuyệt vời, điều này thật tuyệt vời. Câu trả lời chính xác. Chỉ là một lưu ý cho những người đã vấp ngã ở đây. Đếm sẽ đếm tất cả các hàng, tổng sẽ làm tương tự như đếm khi được sử dụng với câu lệnh tình huống.
John Ballinger

1
Giải pháp tuyệt vời! Có lẽ đáng lưu ý rằng phương pháp này hoạt động tốt như vậy nếu bạn kết hợp nhiều bảng với nhau trong một truy vấn, vì sử dụng truy vấn phụ có thể trở nên khá lộn xộn trong trường hợp đó.
Darren Crabb

7
Cảm ơn giải pháp rất thanh lịch này. Btw, điều này cũng hoạt động với TSQL.
Annie Lagang

6
Tại sao điều này có thể không phải là câu trả lời tốt nhất: luôn luôn quét toàn bộ bảng. Xem xét sự tham gia của các truy vấn con, hoặc đếm được lồng trong một lựa chọn. Tuy nhiên, không có chỉ mục nào hiện diện, điều này có thể là tốt nhất vì bạn chỉ đảm bảo quét một bảng so với nhiều bảng. Xem câu trả lời từ @KevinBalmforth
YoYo

1
@JohnBallinger, 'Đếm sẽ đếm tất cả các hàng' - COUNTsẽ tính distributor_idkhôn ngoan. không phải tất cả các hàng của bảng, phải không?
Istiaque Ahmed

88

Một cách làm việc chắc chắn

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:
Xem phân tích hiệu suất của @ KevinBalmforth về lý do tại sao bạn có thể không muốn sử dụng phương pháp này và thay vào đó nên chọn câu trả lời của @ Taryn ♦. Tôi sẽ để điều này để mọi người có thể hiểu các lựa chọn của họ.


2
Điều này giúp tôi giải quyết cách thực hiện nhiều số đếm và xuất chúng trong một câu lệnh CHỌN duy nhất với mỗi số là một cột. Hoạt động tuyệt vời - cảm ơn!
Đánh dấu

2
Tôi đã có thể sử dụng những gì bạn cung cấp ở đây, trong một dự án của tôi. Bây giờ mọi thứ đều nằm trong một Truy vấn duy nhất, thay vì nhiều truy vấn. Trang tải trong chưa đầy một giây, so với 5-8 giây với nhiều truy vấn. Yêu nó. Cảm ơn, Notme.
Wayne Barron

1
Điều này có thể hoạt động tốt nếu mỗi truy vấn phụ thực sự đạt được một chỉ mục. Nếu không, thì sum(case...)nên xem xét giải pháp.
YoYo

1
Lưu ý rằng thay thế cho khác biệt, như tôi đã thực hiện chỉnh sửa, bạn cũng có thể / sử dụng tốt hơn group byvới lợi ích thay thế toàn bộ truy vấn lồng nhau bằng một cách đơn giản count(*)như @Mihai hiển thị - với các đơn giản hóa cú pháp hơn nữa của MySQL.
YoYo

43
SELECT 
    distributor_id, 
    COUNT(*) AS TOTAL, 
    COUNT(IF(level='exec',1,null)),
    COUNT(IF(level='personal',1,null))
FROM sometable;

COUNTchỉ tính non nullcác giá trị và DECODEsẽ trả về giá trị không null 1chỉ khi điều kiện của bạn được thỏa mãn.


distributor_idtruy vấn nào sẽ hiển thị? Nó hiển thị tổng cộng 1 hàng.
Istiaque Ahmed

OP có một nhóm trên cột đã bị bỏ qua trong câu trả lời của tôi.
Majid Laissi

bạn đã cứu mạng tôi, tất cả các aswers khác trả lại nhiều hàng trong MySQL. Cảm ơn rất nhiều
Abner

1
@ Abner rất vui vì điều này vẫn giúp ích sau 8 năm :)
Majid Laissi

@MajidLaissi đúng vậy, đã thay đổi thời gian truy vấn của tôi từ một phút thành ít hơn một giây. :)
Abner

25

Xây dựng trên các câu trả lời được đăng khác.

Cả hai điều này sẽ tạo ra các giá trị đúng:

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 ; 

Tuy nhiên, hiệu suất khá khác nhau, điều này rõ ràng sẽ phù hợp hơn khi số lượng dữ liệu tăng lên.

Tôi thấy rằng, giả sử không có chỉ mục nào được xác định trên bảng, truy vấn sử dụng SUM sẽ thực hiện quét một bảng, trong khi truy vấn với COUNT sẽ thực hiện quét nhiều bảng.

Ví dụ: chạy tập lệnh sau:

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)

Đánh dấu 2 câu lệnh CHỌN và nhấp vào biểu tượng Kế hoạch thực hiện ước tính hiển thị. Bạn sẽ thấy rằng câu lệnh đầu tiên sẽ thực hiện một lần quét bảng và lần thứ hai sẽ thực hiện 4. Rõ ràng một lần quét bảng tốt hơn 4.

Thêm một chỉ số cụm cũng thú vị. Ví dụ

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

CHỌN đầu tiên ở trên sẽ thực hiện Quét chỉ mục cụm đơn. CHỌN thứ hai sẽ thực hiện 4 Tìm kiếm chỉ mục cụm, nhưng chúng vẫn đắt hơn một lần quét chỉ mục cụm. Tôi đã thử điều tương tự trên một cái bàn với 8 triệu hàng và CHỌN thứ hai vẫn đắt hơn rất nhiều.


23

Đối với MySQL, điều này có thể được rút ngắn thành:

SELECT distributor_id,
    COUNT(*) total,
    SUM(level = 'exec') ExecCount,
    SUM(level = 'personal') PersonalCount
FROM yourtable
GROUP BY distributor_id

1
"nhóm của nhà phân
phối_id

2
@ user1451111 câu hỏi ban đầu đã nhận được vì vậy nó là câu trả lời phụ thuộc vào chính câu hỏi
Al-Mothafar

11

Chà, nếu bạn phải có tất cả trong một truy vấn, bạn có thể thực hiện một liên minh:

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';

Hoặc, nếu bạn có thể làm sau khi xử lý:

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

Bạn sẽ nhận được số lượng cho mỗi cấp độ và cần tổng hợp tất cả chúng để có được tổng số.


Tìm thấy UNIONrất hữu ích khi tạo một báo cáo có chứa nhiều phiên bản của COUNT(*)hàm.
James O

Kết quả cho thấy #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

số lượng cột được trả về từ tất cả các truy vấn, trên đó UNION được áp dụng, phải bằng nhau. @IstiaqueAhmed có lẽ đó là lý do đằng sau lỗi của bạn.
dùng1451111

Một lưu ý cho bất cứ ai vấp phải câu trả lời này trong tương lai. Kỹ thuật 'Sau khi xử lý' được mô tả ở đây có thể gây ra sự cố khi một số giá trị trong các cột 'cấp' là NULL. Trong trường hợp đó, tổng của tất cả các tổng số phụ sẽ không bằng tổng số hàng.
user1451111

6

Tôi làm một cái gì đó như thế này trong đó tôi chỉ cần đặt cho mỗi bảng một tên chuỗi để xác định nó trong cột A và đếm cho cột. Sau đó, tôi hợp nhất tất cả để họ xếp chồng lên nhau. Kết quả là khá tốt theo ý kiến ​​của tôi - không chắc nó hiệu quả như thế nào so với các lựa chọn khác nhưng nó đã cho tôi những gì tôi cần.

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;

Kết quả:

-------------------
| String  | Count |
-------------------
| table1  | 123   |
| table2  | 234   |
| table3  | 345   |
| table4  | 456   |
| table5  | 567   |
-------------------

1
a query that I created makes ...- truy vấn đó ở đâu?
Istiaque Ahmed

2
làm thế nào để thêm vị trí caluse vào tất cả các bảng

3

Dựa trên phản hồi được chấp nhận của Bluefeet với một sắc thái được thêm vào bằng cách sử dụng 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

Sử dụng OVER()không có gì trong () sẽ cung cấp cho bạn tổng số cho toàn bộ tập dữ liệu.


1

Tôi nghĩ rằng điều này cũng có thể làm việc cho bạn select count(*) as anc,(select count(*) from Patient where sex='F')as patientF,(select count(*) from Patient where sex='M') as patientM from anc

và bạn cũng có thể chọn và đếm các bảng liên quan như thế này 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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.