Як обчислити відсоток за допомогою оператора 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.