web-dev-qa-db-ja.com

1回のSQLクエリで複数のカウントを取得する方法

このクエリの書き方は疑問に思います。

私はこの実際の構文が偽であることを知っていますが、それはあなたが私が欲しいものを理解するのを助けるでしょう。これはもっと大きなクエリの一部なので、このフォーマットで必要です。

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

これらすべてを1回のクエリで返す必要があります。

また、それは一列に並んでいる必要があるので、以下はうまくいきません。

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

集約関数と一緒にCASEステートメントを使用できます。これは基本的にいくつかのRDBMSのPIVOT関数と同じものです。

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
544
Taryn

確実に機能する1つの方法

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 ;

編集:
なぜこのメソッドを使用したくない場合があり、代わりに@ bluefeetの回答を選択すべきかについては、@ KevinBalmforthのパフォーマンスの詳細を参照してください。人々が彼らの選択肢を理解することができるように、私はこれを残します。

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

COUNTnon null値のみをカウントし、DECODEは条件が満たされた場合にのみnull以外の値1を返します。

35
Majid Laissi

他の投稿された回答を基にしています。

どちらも正しい値を生成します。

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ステートメントを強調表示して、[Display Estimated Execution Plan]アイコンをクリックします。最初のステートメントが1回のテーブルスキャンを実行し、2番目のステートメントが4回のテーブルスキャンを実行することがわかります。明らかに、1回のテーブルスキャンは4回よりも優れています。

クラスタ化インデックスを追加することも興味深いです。例えば。

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

上記の最初のSELECTは、単一のクラスタ化インデックススキャンを実行します。 2番目のSELECTは4つのクラスタ化インデックスシークを行いますが、それでも単一のクラスタ化インデックススキャンよりも高価です。私は800万行のテーブルで同じことを試しましたが、2番目のSELECTはまだもっとずっと高価でした。

20
Kevin Balmforth

MySQLの場合、これは短くすることができます

select distributor_id,
    count(*) total,
    sum(level = 'exec') ExecCount,
    sum(level = 'personal') PersonalCount
from yourtable
group by distributor_id
19
Mihai

1つのクエリにすべて含める必要がある場合は、結合を実行できます。

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;

あなたは各レベルのカウントを取得し、合計を取得するためにそれらをすべて合計する必要があります。

10
CrazyCasta

このようにして、各テーブルに列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   |
-------------------
4
Frantumn

OVER()を使用してニュアンスを追加したBluefeetの受け入れられた応答に基づく

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
mentorrory

私はこれもあなたのために働くことができると思います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

0
Sinte