SQLステートメントを使用してパーセンテージを計算する方法


177

ユーザーとその成績を含むSQL Serverテーブルがあります。わかりやすくするために、2つの列があるとしましょう- namegrade。したがって、一般的な行は、名前: "John Doe"、グレード: "A"になります。

可能なすべての回答のパーセンテージを見つける1つの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
これは、結果の整数.SUMで結果を与える100に等しくない
サンダー

10
テーブルが2回スキャンされるため、最も効率的ではありません。また、参照されるテーブルが複数ある場合、クエリはそれほど単純に見えません。
Alex Aza、

14
@Thunderでは、10進数の値を100から100.0に変更できます。
ジョセフ

SQLクエリの数学的構文がなぜあなたが通常行うことを期待していないのか、誰かが説明できますか?たとえば、通常の場合、合計を時間で割り、その後で時間を100で割ります。論理的な観点から、これについて真に興味があります。
Digitalsa1nt 2016年

4
@ Digitalsa1nt(100 * 2)/ 4 = 50、(2/4)* 100 = 50は、列挙子が乗算される部分である限り。SQLステートメントの優先順位により、同じになります。ただし、データ型が原因で100を使用した場合でも、結果を小数点以下0桁に丸めることができます。除算演算の後に配置する場合と同様に、処理できるデータ型にキャストする必要があります。それ以外の場合、小数点以下は100または0になり、実際のパーセンテージにはなりません
Matt

231
  1. 最も効率的です(over()を使用)。

    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で完全に機能しました。確認のために計算を行いました。小数点第2位で四捨五入するために、CAST(count()* 100.0 / sum(count())over()AS DECIMAL(18、2))を使用しました。投稿ありがとうございます!
RJB 2013年

3
100乗算の場合、あなたがオーバーフロー(例えば算術オーバーフローエラーがデータ型intに式を変換する)、代わりに分母で除算してそれを置き換える:cast((count(*) / (sum(count(*)) over() / 100)) AS DECIMAL(18, 2)) as Percentage
ニキータG.

@RJB出力を10進数としてキャストするときに、100だけでなく100.0を掛ける必要があるのはなぜですか。
AS91、2016

2
@ AS91、10進数へのキャストは除算演算の後に行われるため。int(100)のままにした場合、別のintで除算すると、結果もintになり、結果が丸められます。そのため、トリックは常に実際の除算の前に被除数にキャストを強制することです(1.0などのリテラル小数を乗算するか、キャスト/変換することができます)
luiggig

オプション1はover()Postgresql 10
James Daily

40

別個のCTEを使用して合計を取得する代わりに、「partition by」句なしでウィンドウ関数を使用できます。

使用している場合:

count(*)

グループの数を取得するには、以下を使用できます。

sum(count(*)) over ()

合計数を取得します。

例えば:

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

私の経験ではより高速になる傾向がありますが、内部で一時テーブルを使用する場合があると思います( "set statistics io on"で実行しているときに "Worktable"を見たことがあります)。

編集: 私のクエリ例があなたが探しているものであるかどうかはわかりません、私はウィンドウ関数がどのように機能するかを単に示していました。


+1。これは素晴らしい。「テーブル」の代わりに選択ステートメントがある場合にも使用できます。
mr_georg 2009

1
tempdb作業テーブルであるスプールを使用します。論理読み取りは高いように見えますが、通常とは異なる方法でカウントされます
Martin Smith、

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。
ジョナサンレフラー

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
外部選択に集計( 'sum(CountofGrade)')があるので、それにgroup by句も必要ではありませんか?標準SQLでは、「/(SELECT COUNT(*)FROM Grades)」を使用して総計を取得できると思います。
ジョナサンレフラー

IBM Informix Dynamic Serverは、select-list内のネイキッドSUMを好みません(ただし、文句を言うと、あまり役に立たないメッセージを表示します)。私の回答と以前のコメントで述べたように、選択リストで完全な副選択式を使用してもIDSでは機能します。
ジョナサンレフラー

複雑なwhereを内部クエリに適用できるため、これも優れています。
mvmn

9

パーセンテージを計算する必要があるときはいつでもこれを使用します。

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

100.0は小数を返しますが、ROUND()関数を使用しても、100はそれ自体で結果を最も近い整数に切り上げることに注意してください。


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として返されます-適切な答えを表示するために、ある種の数値のフォーマットまたは変換を行う必要がありますか?
アレックス

1
グレードの選択、round(Count(grade)* 100.0 /((Select Count(grade)From grades)* 1.0)、2)グレードからsql-server returendにラウンド関数を追加するためのグレード別Group By例: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-serverに整数パーセントを与える
Thunder

@サンダー:面白い; たとえば、100から100.00に変更するとどうなりますか?
Jonathan Leffler、2010年

結果は100.0の10進数であることを確認してください
Thunder

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

fromクエリで副選択を使用できます(テストされておらず、どちらが速いかわかりません)。

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

これと同様の問題がありました。100ではなく1.0を掛けると正しい結果が得られるはずです。添付の​​サンプル画像をご覧ください

グレードを選択、(Count(Grade)* 1.0 /(MyTableからCount(*)を選択))をグレード別のMyTableグループからのスコアとして選択 添付の参照画像を参照


どうしても必要な場合を除き、画像として情報を共有しないでください。参照:meta.stackoverflow.com/questions/303812/…
AMC

0

これはMS SQLでうまく機能しています。varcharを、小数点以下2桁に制限された浮動小数点数の結果に変換します。

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.