วิธีการคำนวณเปอร์เซ็นต์ด้วยคำสั่ง SQL


177

ฉันมีตาราง SQL Server ที่มีผู้ใช้และเกรดของพวกเขา เพื่อประโยชน์ของความเรียบง่ายช่วยให้เพียงแค่บอกว่ามี 2 คอลัมน์ - &name gradeดังนั้นแถวทั่วไปจะเป็นชื่อ: "John Doe", Grade: "A"

ฉันกำลังมองหาคำสั่ง SQL หนึ่งคำสั่งที่จะค้นหาเปอร์เซ็นต์ของคำตอบที่เป็นไปได้ทั้งหมด (A, B, C, ฯลฯ ... ) นอกจากนี้ยังมีวิธีการทำเช่นนี้โดยไม่ต้องกำหนดคำตอบที่เป็นไปได้ทั้งหมด (ฟิลด์ข้อความแบบเปิด - ผู้ใช้สามารถป้อน 'ผ่าน / ล้มเหลว', 'ไม่มี' ฯลฯ ... )

ผลลัพธ์สุดท้ายที่ฉันต้องการคือ A: 5%, B: 15%, C: 40%, ฯลฯ ...

คำตอบ:


227

ฉันได้ทดสอบสิ่งต่อไปนี้แล้วและนี่ใช้งานได้ คำตอบของ gordyii อยู่ใกล้ แต่มีการคูณ 100 ในที่ผิดและมีวงเล็บหายไป

Select Grade, (Count(Grade)* 100 / (Select Count(*) From MyTable)) as Score
From MyTable
Group By Grade

21
สิ่งนี้จะให้ผลลัพธ์เป็นจำนวนเต็มผลรวมของผลลัพธ์ไม่เท่ากับ 100
287 Thunder

10
ไม่ใช่ประสิทธิภาพที่สุดเนื่องจากตารางจะถูกสแกนสองครั้ง นอกจากนี้แบบสอบถามจะไม่ดูง่ายหากมีการอ้างอิงมากกว่าหนึ่งตาราง
Alex Aza

14
@Thunder คุณสามารถเปลี่ยน 100 เป็น 100.0 สำหรับค่าทศนิยม
โจเซฟ

บางคนสามารถอธิบายได้ว่าทำไมไวยากรณ์ทางคณิตศาสตร์ของการสืบค้น SQL ไม่ใช่สิ่งที่คุณคาดหวังว่าจะทำได้ตามปกติ เช่นปกติฉันจะหารด้วยผลรวมแล้วคูณด้วย 100? อย่างแท้จริงอยากรู้เกี่ยวกับเรื่องนี้จากจุดยืนที่มีเหตุผล
Digitalsa1nt

4
@ Digitalsa1nt (100 * 2) / 4 = 50, (2/4) * 100 = 50 ตราบใดที่ตัวแจงนับเป็นส่วนที่ถูกคูณ เนื่องจากความสำคัญของคำสั่ง SQL มันจะเหมือนกัน อย่างไรก็ตามเนื่องจากชนิดข้อมูลหากใช้ 100 คุณยังสามารถรับผลลัพธ์เป็น 0 ทศนิยมที่คุณต้องการ% ซึ่งราวกับว่าคุณใส่มันหลังจากการดำเนินการหารคุณจะต้องแน่ใจว่าคุณส่งไปยังชนิดข้อมูลที่สามารถจัดการได้ ตำแหน่งทศนิยมมิฉะนั้นคุณจะลงท้ายด้วย 100 หรือ 0 และจะไม่เป็นเปอร์เซ็นต์จริง
Matt

232
  1. มีประสิทธิภาพมากที่สุด (ใช้เกิน ())

    select Grade, count(*) * 100.0 / sum(count(*)) over()
    from MyTable
    group by Grade
  2. สากล (เวอร์ชัน SQL ใด ๆ )

    select Grade, count(*) * 100.0 / (select count(*) from MyTable)
    from MyTable
    group by Grade;
  3. ด้วย CTE ที่มีประสิทธิภาพน้อยที่สุด

    with t(Grade, GradeCount) 
    as 
    ( 
        select Grade, count(*) 
        from MyTable
        group by Grade
    )
    select Grade, GradeCount * 100.0/(select sum(GradeCount) from t)
    from t;

13
มากกว่า () ทำงานได้อย่างสมบูรณ์บน SQL Server 2008 ของฉันฉันทำคณิตศาสตร์เพื่อยืนยัน เพื่อปัดเศษเป็นทศนิยม 2 ตำแหน่งฉันใช้ CAST (count ( ) * 100.0 / sum (count ( )) เหนือ () AS DECIMAL (18, 2)) ขอบคุณสำหรับการโพสต์!
RJB

3
ในกรณีที่คุณล้นในการคูณ 100 (เช่นข้อผิดพลาดทางคณิตศาสตร์มากเกินไปการแปลงนิพจน์เป็นประเภทข้อมูล int ) ให้แทนที่ด้วยการหารในส่วนแทน:cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage
Nikita G.

@RJB ทำไมคุณต้องคูณด้วย 100.0 และไม่ใช่แค่ 100 เมื่อคุณส่งออกเป็นทศนิยม?
AS91

2
@ AS91 เนื่องจากการคาสต์เป็นทศนิยมเกิดขึ้นหลังจากการดำเนินการหาร หากคุณปล่อยให้ int (100) การหารด้วย int อื่นจะส่งผลให้ int เช่นกันซึ่งจะปัดเศษผลลัพธ์ นั่นคือเหตุผลที่กลโกงบังคับให้นักแสดงรับเงินปันผลเสมอก่อนการหารจริง (คุณสามารถคูณด้วยทศนิยมตามตัวอักษรเช่น 1.0 หรือร่าย / แปลง)
luiggig

ตัวเลือกที่ 1 พร้อมover()ผลงานยอดเยี่ยมใน Postgresql 10
James Daily

40

แทนที่จะใช้ CTE แยกเพื่อรับผลรวมคุณสามารถใช้ฟังก์ชั่นหน้าต่างโดยไม่มีข้อ "พาร์ทิชันโดย"

หากคุณกำลังใช้:

count(*)

ในการรับจำนวนของกลุ่มคุณสามารถใช้:

sum(count(*)) over ()

เพื่อรับจำนวนทั้งหมด

ตัวอย่างเช่น:

select Grade, 100. * count(*) / sum(count(*)) over ()
from table
group by Grade;

มันอาจจะเร็วกว่าในประสบการณ์ของฉัน แต่ฉันคิดว่ามันอาจใช้ตารางชั่วคราวในบางกรณี (ฉันเคยเห็น "Worktable" เมื่อทำงานด้วย "set Statistics io on")

แก้ไข: ฉันไม่แน่ใจว่าแบบสอบถามตัวอย่างของฉันเป็นสิ่งที่คุณกำลังมองหาฉันแค่แสดงให้เห็นถึงการทำงานของหน้าต่าง


+1 มันเยี่ยมมาก นอกจากนี้ยังสามารถนำมาใช้หากแทนที่ 'ตาราง' มีคำสั่งที่เลือก
mr_georg

1
มันใช้สปูลtempdbซึ่งเป็นตารางงาน การอ่านแบบลอจิคัลดูเหมือนจะสูงกว่าแต่พวกเขาจะถูกนับแตกต่างจากปกติ
มาร์ตินสมิ ธ

1
ที่จริงแล้วCOUNT(*) OVER ()ในข้อความค้นหาของคุณจะส่งคืนตัวเลขที่ไม่เกี่ยวข้องอย่างสมบูรณ์ (โดยเฉพาะจำนวนแถวของชุดผลลัพธ์ที่จัดกลุ่ม ) คุณควรใช้SUM(COUNT(*)) OVER ()แทน
Andriy M

10

คุณต้องคำนวณผลรวมของคะแนนหากเป็น SQL 2005 คุณสามารถใช้ CTE

    WITH Tot(Total) (
    SELECT COUNT(*) FROM table
    )
    SELECT Grade, COUNT(*) / Total * 100
--, CONVERT(VARCHAR, COUNT(*) / Total * 100) + '%'  -- With percentage sign
--, CONVERT(VARCHAR, ROUND(COUNT(*) / Total * 100, -2)) + '%'  -- With Round
    FROM table
    GROUP BY Grade

1
แน่นอนว่านี่จะให้เปอร์เซ็นต์สำหรับรหัสเกรดที่มีอยู่ในตารางเท่านั้นไม่ใช่สำหรับรหัสที่อาจมีอยู่และไม่ใช่ แต่หากไม่มีรายการที่ชัดเจนของรหัสเกรดที่เกี่ยวข้อง (ถูกต้อง) คุณจะไม่สามารถทำได้ดีขึ้น ดังนั้น +1 จากฉัน
โจนาธาน Leffler

1
อัญมณีที่ซ่อนไว้สำหรับฉันคือคุณได้แสดงความคิดเห็นแปลง
Chris Catignani

9

คุณต้องจัดกลุ่มในเขตข้อมูลเกรด แบบสอบถามนี้ควรให้สิ่งที่คุณต้องการในฐานข้อมูลใด ๆ

    Select Grade, CountofGrade / sum(CountofGrade) *100 
    from
    (
    Select Grade, Count(*) as CountofGrade
    From Grades
    Group By Grade) as sub
    Group by Grade

คุณควรระบุระบบที่คุณใช้


2
เนื่องจากคุณมีการรวม ('ผลรวม (CountofGrade)') ในตัวเลือกด้านนอกคุณไม่ต้องการกลุ่มตามข้อในหรือไม่ และใน SQL มาตรฐานฉันคิดว่าคุณสามารถใช้ '/ (SELECT COUNT (*) จาก Grades)' เพื่อรับผลรวมทั้งหมด
Jonathan Leffler

IBM Informix Dynamic Server ไม่ชอบ SUM เปล่าใน select-list (แม้ว่าจะให้ข้อความที่ไม่ค่อยมีประโยชน์เมื่อบ่น) ดังที่ระบุไว้ในคำตอบและความคิดเห็นก่อนหน้าของฉันการใช้นิพจน์เลือกย่อยแบบเต็มในรายการเลือกทำงานใน IDS
โจนาธาน Leffler

นอกจากนี้ยังดีกว่าเพราะสามารถใช้ซับซ้อนที่แบบสอบถามภายใน
mvmn

9

ฉันใช้สิ่งนี้เมื่อฉันต้องการเปอร์เซ็นต์

ROUND(CAST((Numerator * 100.0 / Denominator) AS FLOAT), 2) AS Percentage

โปรดทราบว่า 100.0 จะส่งกลับทศนิยมในขณะที่ 100 ตัวจะได้ผลลัพธ์เป็นจำนวนเต็มที่ใกล้ที่สุดแม้จะมีฟังก์ชัน ROUND ()!


7

ต่อไปนี้ควรทำงาน

ID - Key
Grade - A,B,C,D...

แก้ไข: ย้าย* 100และเพิ่ม1.0เพื่อให้แน่ใจว่าจะไม่ทำการหารจำนวนเต็ม

Select 
   Grade, Count(ID) * 100.0 / ((Select Count(ID) From MyTable) * 1.0)
From MyTable
Group By Grade

1
ใช้งานได้ แต่คำตอบทั้งหมดกลับมาเป็น 0 ฉันต้องทำการจัดรูปแบบตัวเลขหรือการแปลงเพื่อดูคำตอบที่เหมาะสมหรือไม่
Alex

1
เลือกเกรด, รอบ (นับ (เกรด) * 100.0 / ((เลือกจำนวน (เกรด) จากเกรด) * 1.0), 2) จากคะแนนกลุ่มตามเกรดเพื่อเพิ่มฟังก์ชันรอบในการกู้คืน sql-server เช่น: 21.56000000000
Thunder

5

นี่คือฉันเชื่อว่าเป็นโซลูชันทั่วไปแม้ว่าฉันทดสอบโดยใช้ IBM Informix Dynamic Server 11.50.FC3 แบบสอบถามต่อไปนี้:

SELECT grade,
       ROUND(100.0 * grade_sum / (SELECT COUNT(*) FROM grades), 2) AS pct_of_grades
    FROM (SELECT grade, COUNT(*) AS grade_sum
            FROM grades
            GROUP BY grade
         )
    ORDER BY grade;

ให้เอาต์พุตต่อไปนี้กับข้อมูลการทดสอบที่แสดงด้านล่างกฎแนวนอน ROUNDฟังก์ชั่นอาจจะ DBMS เฉพาะ แต่ส่วนที่เหลือ (อาจ) ไม่ได้ (โปรดทราบว่าฉันเปลี่ยน 100 เป็น 100.0 เพื่อให้แน่ใจว่าการคำนวณเกิดขึ้นโดยใช้จำนวนเต็ม - DECIMAL, NUMERIC - เลขคณิต; ดูความคิดเห็นและขอบคุณ Thunder)

grade  pct_of_grades
CHAR(1) DECIMAL(32,2)
A       32.26
B       16.13
C       12.90
D       12.90
E       9.68
F       16.13

CREATE TABLE grades
(
    id VARCHAR(10) NOT NULL,
    grade CHAR(1) NOT NULL CHECK (grade MATCHES '[ABCDEF]')
);

INSERT INTO grades VALUES('1001', 'A');
INSERT INTO grades VALUES('1002', 'B');
INSERT INTO grades VALUES('1003', 'F');
INSERT INTO grades VALUES('1004', 'C');
INSERT INTO grades VALUES('1005', 'D');
INSERT INTO grades VALUES('1006', 'A');
INSERT INTO grades VALUES('1007', 'F');
INSERT INTO grades VALUES('1008', 'C');
INSERT INTO grades VALUES('1009', 'A');
INSERT INTO grades VALUES('1010', 'E');
INSERT INTO grades VALUES('1001', 'A');
INSERT INTO grades VALUES('1012', 'F');
INSERT INTO grades VALUES('1013', 'D');
INSERT INTO grades VALUES('1014', 'B');
INSERT INTO grades VALUES('1015', 'E');
INSERT INTO grades VALUES('1016', 'A');
INSERT INTO grades VALUES('1017', 'F');
INSERT INTO grades VALUES('1018', 'B');
INSERT INTO grades VALUES('1019', 'C');
INSERT INTO grades VALUES('1020', 'A');
INSERT INTO grades VALUES('1021', 'A');
INSERT INTO grades VALUES('1022', 'E');
INSERT INTO grades VALUES('1023', 'D');
INSERT INTO grades VALUES('1024', 'B');
INSERT INTO grades VALUES('1025', 'A');
INSERT INTO grades VALUES('1026', 'A');
INSERT INTO grades VALUES('1027', 'D');
INSERT INTO grades VALUES('1028', 'B');
INSERT INTO grades VALUES('1029', 'A');
INSERT INTO grades VALUES('1030', 'C');
INSERT INTO grades VALUES('1031', 'F');

ให้ร้อยละจำนวนเต็มใน SQL เซิร์ฟเวอร์
ทันเดอร์

@ ทันเดอร์: น่าสนใจ; จะเกิดอะไรขึ้นถ้าคุณเปลี่ยนพูด 100 ถึง 100.00
Jonathan Leffler

แน่ใจว่าผลลัพธ์เป็นทศนิยมด้วย 100.0
Thunder


3

ในเวอร์ชันเซิร์ฟเวอร์ sql ใด ๆ คุณสามารถใช้ตัวแปรสำหรับผลรวมคะแนนทั้งหมดเช่นนี้:

declare @countOfAll decimal(18, 4)
select @countOfAll = COUNT(*) from Grades

select
Grade,  COUNT(*) / @countOfAll * 100
from Grades
group by Grade

3

คุณสามารถใช้ตัวเลือกย่อยในการสอบถามของคุณ (ยังไม่ทดลองและไม่แน่ใจว่าเร็วกว่า):

SELECT Grade, COUNT(*) / TotalRows
FROM (SELECT Grade, COUNT(*) As TotalRows
      FROM myTable) Grades
GROUP BY Grade, TotalRows

หรือ

SELECT Grade, SUM(PartialCount)
FROM (SELECT Grade, 1/COUNT(*) AS PartialCount
      FROM myTable) Grades
GROUP BY Grade

หรือ

SELECT Grade, GradeCount / SUM(GradeCount)
FROM (SELECT Grade, COUNT(*) As GradeCount
      FROM myTable
      GROUP BY Grade) Grades

นอกจากนี้คุณยังสามารถใช้กระบวนงานที่เก็บไว้ (ขอโทษสำหรับไวยากรณ์ Firebird):

SELECT COUNT(*)
FROM myTable
INTO :TotalCount;

FOR SELECT Grade, COUNT(*)
FROM myTable
GROUP BY Grade
INTO :Grade, :GradeCount
DO
BEGIN
    Percent = :GradeCount / :TotalCount;
    SUSPEND;
END

0

ฉันมีปัญหาที่คล้ายกันนี้ คุณควรจะได้ผลลัพธ์ที่ถูกต้องคูณด้วย 1.0 แทนที่จะเป็น 100 ดูตัวอย่างรูปภาพที่แนบมา

เลือกเกรด (นับ (เกรด) * 1.0 / (เลือกจำนวน (*) จาก MyTable)) เป็นคะแนนจาก MyTable Group ตามเกรด ดูภาพอ้างอิงที่แนบมา


กรุณาอย่าแชร์ข้อมูลเป็นภาพเว้นแต่จำเป็นจริงๆ ดู: meta.stackoverflow.com/questions/303812/...
AMC

0

อันนี้ทำงานได้ดีใน MS SQL มันเปลี่ยน varchar ให้เป็นผลลัพธ์ของทศนิยมสองตำแหน่งทศนิยม จำกัด

Select field1, cast(Try_convert(float,(Count(field2)* 100) / 
Try_convert(float, (Select Count(*) From table1))) as decimal(10,2)) as new_field_name 
From table1 
Group By field1, field2;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.