วิธีรับจำนวนการนับด้วยแบบสอบถาม SQL หนึ่งรายการ


316

ฉันสงสัยว่าจะเขียนคำค้นหานี้อย่างไร

ฉันรู้ว่าไวยากรณ์ที่แท้จริงนี้เป็นของปลอม แต่มันจะช่วยให้คุณเข้าใจสิ่งที่ฉันต้องการ ฉันต้องการมันในรูปแบบนี้เพราะมันเป็นส่วนหนึ่งของแบบสอบถามที่ใหญ่กว่ามาก

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

ฉันต้องการสิ่งนี้ทั้งหมดกลับมาในแบบสอบถามเดียว

นอกจากนี้ต้องอยู่ในแถวเดียวดังนั้นสิ่งต่อไปนี้จะไม่ทำงาน:

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

1
แบบสอบถามนี้เกี่ยวกับคุณทำงานถูกต้องหรือไม่? SELECT distributor_id, COUNT(*) AS TOTAL, COUNT(*) WHERE level = 'exec', COUNT(*) WHERE level = 'personal'
Pratik

คำตอบ:


690

คุณสามารถใช้CASEคำสั่งที่มีฟังก์ชั่นรวม นี่เป็นสิ่งเดียวกับPIVOTฟังก์ชั่นใน 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
น่าอัศจรรย์นี่วิเศษมาก คำตอบที่ดี เพียงบันทึกถึงผู้ที่ได้สะดุดที่นี่ การนับจะนับแถวทั้งหมดผลรวมจะทำเช่นเดียวกันกับการนับเมื่อใช้กับคำสั่ง case
John Ballinger

1
ทางออกที่ยอดเยี่ยม! อาจเป็นเรื่องที่น่าสังเกตว่าวิธีการนี้ใช้งานได้ดีหากคุณรวมหลายตารางเข้าด้วยกันในการสืบค้นเดียวเนื่องจากการใช้การสืบค้นย่อยอาจทำให้เกิดความสับสนในกรณีดังกล่าว
Darren Crabb

7
ขอบคุณสำหรับการแก้ปัญหาที่สง่างามมาก Btw สิ่งนี้ใช้ได้กับ TSQL ด้วย
Annie Lagang

6
ทำไมนี่อาจไม่ใช่คำตอบที่ดีที่สุด: สแกนแบบเต็มตารางเสมอ พิจารณาการเข้าร่วมของการค้นหาย่อย - ย่อยหรือการนับซ้อนในการเลือก อย่างไรก็ตามหากไม่มีดัชนีแสดงอยู่สิ่งนี้อาจดีที่สุดเนื่องจากคุณรับประกันการสแกนตารางเดียวและหลายรายการ ดูคำตอบจาก @KevinBalmforth
YoYo

1
@ JohnBallinger 'Count จะนับทุกแถว' - COUNTจะนับรวมด้วยdistributor_idปัญญา ไม่ใช่ทุกแถวของตารางใช่มั้ย
Istiaque Ahmed

88

วิธีหนึ่งที่ใช้งานได้อย่างแน่นอน

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 ;

แก้ไข:
ดูการแยกย่อยของประสิทธิภาพ @ KevinBalmforth สำหรับสาเหตุที่คุณไม่ต้องการใช้วิธีนี้และควรเลือกใช้คำตอบของ @ Taryn ♦ ฉันกำลังออกจากที่นี่เพื่อให้ผู้คนสามารถเข้าใจตัวเลือกของพวกเขา


2
สิ่งนี้ช่วยฉันแก้ไขวิธีการนับหลายครั้งและส่งออกในคำสั่ง SELECT เดียวโดยแต่ละการนับเป็นคอลัมน์ ใช้งานได้ดี - ขอบคุณ!
ทำเครื่องหมาย

2
ฉันสามารถใช้สิ่งที่คุณให้ไว้ที่นี่ในโครงการของฉัน ตอนนี้ทุกอย่างอยู่ในแบบสอบถามเดียวแทนที่จะเป็นหลายแบบสอบถาม หน้าเว็บโหลดในเวลาน้อยกว่าหนึ่งวินาทีเทียบกับ 5-8 วินาทีที่มีหลายข้อความค้นหา รักมัน ขอบคุณ Notme
Wayne Barron

1
สิ่งนี้อาจทำงานได้ดีถ้าแบบสอบถามย่อยแต่ละรายการเข้าชมดัชนีจริง ถ้าไม่เช่นนั้นsum(case...)ควรพิจารณาวิธีแก้ปัญหา
YoYo

1
โปรดทราบว่าเป็นทางเลือกที่แตกต่างอย่างที่ฉันได้ทำการแก้ไขคุณยังสามารถ / ใช้group byประโยชน์ได้ดีขึ้นด้วยการแทนที่การสืบค้นที่ซ้อนกันทั้งหมดด้วยการcount(*)แสดงเป็น @Mihai แบบเรียบง่ายด้วยการทำให้ไวยากรณ์ของ MySQL ง่ายขึ้นเท่านั้น
YoYo

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

COUNTนับเฉพาะnon nullค่าและDECODEจะส่งคืนค่าที่ไม่เป็นโมฆะ1หากเงื่อนไขของคุณเป็นที่พอใจเท่านั้น


ซึ่งdistributor_idจะแสดงการค้นหา? มันแสดงให้เห็นทั้งหมด 1 แถว
Istiaque Ahmed

OP มีกลุ่มโดยในคอลัมน์ที่ถูกตัดออกไปในคำตอบของฉัน
Majid Laissi

คุณช่วยชีวิตฉันไว้คนอื่น ๆ ทั้งหมดกลับมาหลายแถวใน MySQL ขอบคุณมาก
อับเนอร์

1
@Abner ดีใจที่สิ่งนี้ยังคงช่วยหลังจาก 8 ปี :)
Majid Laissi

@MajidLaissi ใช่แล้วเปลี่ยนเวลาค้นหาของฉันจากนาทีเป็นน้อยกว่าหนึ่งวินาที :)
อับเนอร์

25

การสร้างคำตอบที่โพสต์อื่น ๆ

ทั้งสองอย่างนี้จะสร้างคุณค่าที่เหมาะสม:

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 ; 

อย่างไรก็ตามประสิทธิภาพมีความแตกต่างกันอย่างเห็นได้ชัดซึ่งจะมีความเกี่ยวข้องมากขึ้นเมื่อปริมาณข้อมูลเพิ่มขึ้น

ฉันพบว่าหากไม่มีดัชนีที่กำหนดไว้ในตารางแบบสอบถามที่ใช้ SUM จะทำการสแกนตารางเดียวในขณะที่แบบสอบถามที่มี COUNT จะทำการสแกนตารางหลายรายการ

ตัวอย่างรันสคริปต์ต่อไปนี้:

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)

ไฮไลต์คำสั่ง 2 SELECT และคลิกที่ไอคอนแสดงแผนดำเนินการโดยประมาณ คุณจะเห็นว่าคำสั่งแรกจะทำการสแกนหนึ่งตารางและที่สองจะทำ 4 เห็นได้ชัดว่าการสแกนหนึ่งตารางนั้นดีกว่า 4

การเพิ่มดัชนีคลัสเตอร์ยังน่าสนใจเช่นกัน เช่น

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

SELECT แรกด้านบนจะทำการสแกนดัชนีแบบกลุ่มเดียว SELECT ตัวที่สองจะทำการค้นหาดัชนีแบบกลุ่ม 4 ครั้ง แต่ยังคงมีราคาแพงกว่าการสแกนดัชนีแบบกลุ่มเดียว ฉันลองแบบเดียวกันบนโต๊ะที่มีแถว 8 ล้านแถวและตัวเลือกที่สองก็ยังมีราคาแพงกว่ามาก


23

สำหรับ MySQL สิ่งนี้สามารถย่อให้เป็น:

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

1
เป็น "กลุ่มโดย Distributor_id" "จำเป็นจริงๆในการค้นหานี้หรือไม่มันสามารถทำงานได้โดยไม่ต้องใช้เช่นกัน
user1451111

2
@ user1451111 คำถามเดิมได้มันจึงเป็นคำตอบขึ้นอยู่กับคำถามของตัวเอง
Al-Mothafar

11

ถ้าคุณต้องมีทุกอย่างในแบบสอบถามคุณสามารถทำการรวมกันได้:

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

หรือถ้าคุณสามารถทำได้หลังจากการประมวลผล:

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

คุณจะได้รับการนับในแต่ละระดับและจำเป็นต้องรวมพวกเขาทั้งหมดเพื่อให้ได้ผลรวม


พบUNIONว่ามีประโยชน์มากเมื่อสร้างรายงานที่มีCOUNT(*)ฟังก์ชันหลายอินสแตนซ์
James O

#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

จำนวนคอลัมน์ที่ส่งคืนจากคิวรีทั้งหมดที่ใช้กับ UNION ควรเท่ากัน @IstiaqueAhmed อาจเป็นสาเหตุของข้อผิดพลาดของคุณ
user1451111

หมายเหตุสำหรับผู้ที่สะดุดกับคำตอบนี้ในอนาคต เทคนิค 'หลังจากประมวลผล' ที่อธิบายไว้ที่นี่อาจทำให้เกิดปัญหาเมื่อบางค่าในคอลัมน์ 'ระดับ' เป็นค่า NULL ในกรณีนั้นผลรวมของการนับทั้งหมดจะไม่เท่ากับจำนวนแถวทั้งหมด
user1451111

6

ฉันทำสิ่งนี้ที่ฉันให้ชื่อสตริงแต่ละตารางเพื่อระบุในคอลัมน์ A และนับสำหรับคอลัมน์ จากนั้นฉันรวมพวกมันทั้งหมด ผลที่ได้ค่อนข้างสวยในความคิดของฉัน - ไม่แน่ใจว่ามันมีประสิทธิภาพเมื่อเทียบกับตัวเลือกอื่น ๆ แต่มันทำให้ฉันได้สิ่งที่ฉันต้องการ

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;

ผลลัพธ์:

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

1
a query that I created makes ...- แบบสอบถามนั้นอยู่ที่ไหน
Istiaque Ahmed

2
วิธีเพิ่มที่ที่ตารางไปยังตารางทั้งหมด

3

ตามการตอบรับที่ยอมรับของ Bluefeet พร้อมด้วยความแตกต่างที่เพิ่มขึ้นโดยใช้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

ใช้OVER()กับไม่มีอะไรใน () จะให้คุณนับรวมสำหรับชุดข้อมูลทั้งหมด


1

ฉันคิดว่านี่สามารถใช้ได้กับคุณด้วย select count(*) as anc,(select count(*) from Patient where sex='F')as patientF,(select count(*) from Patient where sex='M') as patientM from anc

และคุณยังสามารถเลือกและนับตารางที่เกี่ยวข้องเช่นนี้ 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.