Як обчислити відсоток за допомогою оператора SQL


177

У мене є таблиця SQL Server, яка містить користувачів та їхні оцінки. Для простоти скажемо, що є 2 стовпці - name& grade. Таким типовим рядком буде ім'я: "Джон Доу", клас: "А".

Я шукаю одне твердження SQL, яке знайде відсотки всіх можливих відповідей. (A, B, C і т. Д. ...) Також, чи можна це зробити, не визначаючи всіх можливих відповідей (відкрите текстове поле - користувачі можуть ввести "пропустити / провалити", "немає" тощо).

Кінцевий вихід, який я шукаю, - це: 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.
Грім,

10
Не найефективніше, оскільки таблиця буде відсканована двічі. Також запит не буде виглядати таким простим, якщо на них посилається більше однієї таблиці.
Олексій Аза

14
@Thunder ви можете змінити 100 до 100,0 для десяткових значень.
Йосиф

Чи може хтось пояснити, чому математичний синтаксис запиту SQL - це не те, що ви очікували б робити нормально? Наприклад, нормально, я б поділив на загальну кількість разів тоді 100? По-справжньому цікаво про це з логічної точки зору.
Digitalsa1nt

4
@ Digitalsa1nt (100 * 2) / 4 = 50, (2/4) * 100 = 50 до тих пір, поки перелічувач - це частина, що множиться. Через перевагу операторів SQL він буде таким же. однак, через типи даних, якщо використовуючи 100, ви все одно можете отримати результат округлення до 0 десяткових знаків, на який ви хочете%, де, як якщо б ви поставили його після операції поділу, вам доведеться переконатися, що ви перейдете до типу даних, який може обробляти десяткових знаків у протилежному випадку ви отримаєте 100 або 0 і ніколи не будете фактичним відсотком
Метт

231
  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
over () відмінно працював на моєму SQL Server 2008, я зробив математику для підтвердження. Для того, щоб її округлити до двох знаків після коми, я використав CAST (count ( ) * 100.0 / sum (count ( )) over () AS DECIMAL (18, 2)). Дякую за пост!
RJB

3
Якщо ви переповнюєте на множення 100 (наприклад, арифметична помилка переповнення, перетворюючи вираз у тип даних int ), замініть його поділом у знаменнику замість цього:cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage
Микита Г.

@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" під час роботи з "встановити статистику io on").

EDIT: Я не впевнений, чи є в моєму прикладі запиту те, що ви шукаєте, я просто ілюстрував, як працюють функції вікон.


+1. Це чудово. Його також можна використовувати, якщо замість 'table' є оператор select.
mr_georg

1
Тут використовується золотник, в tempdbякому знаходиться робочий стіл. Логічні показання здаються вищими, але вони рахуються інакше, ніж звичайні
Мартін Сміт

1
Насправді, COUNT(*) OVER ()у вашому запиті повернеться абсолютно неспоріднена цифра (конкретно, кількість рядків згрупованого набору результатів). Ви повинні використовувати SUM(COUNT(*)) OVER ()замість цього.
Андрій М

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 від мене.
Джонатан Леффлер

1
Прихований дорогоцінний камінь для мене - це ви прокоментували CONVERT.
Кріс Катіньяні

9

Вам потрібно згрупуватись на полі класу Цей запит повинен дати вам те, що ви шукаєте в майже будь-якій базі даних.

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

Ви повинні вказати систему, яку ви використовуєте.


2
Оскільки у вас є сукупність ('sum (CountofGrade)') у зовнішньому виділенні, чи не потрібна вам також група за пунктом? І в стандартному SQL, я думаю, ви могли б використовувати "/ (SELECT COUNT (*) FROM OFGRES") для отримання загальної суми.
Джонатан Леффлер

Динамічний сервер IBM Informix не любить голий SUM у списку вибору (хоча він подає дещо менш корисне повідомлення, коли скаржиться). Як було зазначено у моїй відповіді та попередньому коментарі, використання вираз із повним підбором у списку вибору працює в IDS.
Джонатан Леффлер

Це також краще, тому що можна застосувати комплексне місце до внутрішнього запиту.
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...

EDIT: перемістив * 100та додав значення, 1.0щоб переконатися, що воно не здійснює цілого поділу

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

1
це працює, але всі відповіді повертаються як 0 - чи потрібно мені робити якесь форматування чи перетворення чисел, щоб побачити правильну відповідь?
Алекс

1
Виберіть Оцінка, кругла (Підрахунок (оцінка) * 100,0 / ((Виберіть підрахунок (оцінка) від класів) * 1,0), 2) З класів Групу за класом для додавання функції кругообігу у відтворенні sql-сервера, наприклад: 21.56000000000
Грома

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функція може бути специфічною для СУБД, але решта (ймовірно) - ні. (Зауважте, що я змінив 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-сервері
Гром,

@Thunder: цікаво; що станеться, якщо змінити, скажімо, 100 на 100,00?
Джонатан Леффлер

Впевнений, що результат у десятковій формі з 100.0
Грім,

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

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 за класом Дивіться опорне зображення, що додається


Будь ласка, не діліться інформацією як зображення, якщо це абсолютно не потрібно. Дивіться: meta.stackoverflow.com/questions/303812/… .
AMC

0

Цей добре працює в MS SQL. Він перетворює варчар у результат поплавця з обмеженням на два десяткових місця.

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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.