web-dev-qa-db-ja.com

Redshift / Postgresでは、条件を満たす行をカウントする方法は?

条件を満たす行のみをカウントするクエリを作成しようとしています。

たとえば、MySQLでは次のように記述します。

_SELECT
    COUNT(IF(grade < 70), 1, NULL)
FROM
    grades
ORDER BY
    id DESC;
_

ただし、Redshiftでこれを実行しようとすると、次のエラーが返されます。

エラー:関数if(boolean、integer、 "unknown")が存在しません

ヒント:指定された名前と引数のタイプに一致する関数はありません。明示的な型キャストを追加する必要がある場合があります。

条件付きステートメントのドキュメントを確認し、見つけました

NULLIF(value1, value2)

ただし、value1とvalue2のみを比較し、そのような値が等しい場合はnullを返します。

簡単なIFステートメントが見つかりませんでした。

CASE式を使用しようとしましたが、必要な結果が得られません。

_SELECT 
    CASE
        WHEN grade < 70 THEN COUNT(rank)
        ELSE COUNT(rank)
    END
FROM
   grades
_

これは私が物事を数えたい方法です:

  • 失敗(グレード<70)

  • 平均(70 <=グレード<80)

  • 良い(80 <=グレード<90)

  • 優れている(90 <= grade <= 100)

そして、これは私が結果を見ることを期待する方法です:

_+========+=========+======+===========+
| failed | average | good | excellent |
+========+=========+======+===========+
|   4    |    2    |  1   |     4     |
+========+=========+======+===========+
_

しかし、私はこれを取得しています:

_+========+=========+======+===========+
| failed | average | good | excellent |
+========+=========+======+===========+
|  11    |   11    |  11  |    11     |
+========+=========+======+===========+
_

誰かが私を正しい方向に向けてくれることを願っています!

これが役立つ場合は、サンプル情報をご覧ください

_CREATE TABLE grades(
  grade integer DEFAULT 0,
);

INSERT INTO grades(grade) VALUES(69, 50, 55, 60, 75, 70, 87, 100, 100, 98, 94);
_
42
ILikeTacos

まず、ここで抱えている問題は、「評点が70未満の場合、このcase式の値はcount(rank)です。それ以外の場合、この式の値はcount(rank)です」ということです。 」したがって、どちらの場合でも、常に同じ値を取得しています。

SELECT 
    CASE
        WHEN grade < 70 THEN COUNT(rank)
        ELSE COUNT(rank)
    END
FROM
   grades

count()はnull以外の値のみをカウントするため、通常、試行していることを達成するために表示されるパターンは次のとおりです。

SELECT 
    count(CASE WHEN grade < 70 THEN 1 END) as grade_less_than_70,
    count(CASE WHEN grade >= 70 and grade < 80 THEN 1 END) as grade_between_70_and_80
FROM
   grades

そうすれば、テスト式がtrueの場合にのみcase式は1に評価され、そうでない場合はnullになります。それからcount()はnull以外のインスタンスのみをカウントします。つまり、テスト式がtrueの場合、必要なものが得られます。

編集:サイドノートとして、これはcount(if(test, true-value, false-value))を使用して元々これを記述した方法とまったく同じであり、count(case when test then true-value end)としてのみ書き換えられることに注意してください(nullはfalse値の意味です) elseがケースに提供されなかったため)。

編集:postgres 9.4は、この最初の交換の数か月後にリリースされました。このバージョンでは集約フィルターが導入され、これによりこのようなシナリオを少し見やすく、明確にすることができます。この答えにはまだいくつかの賛成票がありますので、ここで偶然見つけて新しいpostgres(つまり9.4+)を使用している場合は、この同等のバージョンを検討することをお勧めします。

SELECT
    count(*) filter (where grade < 70) as grade_less_than_70,
    count(*) filter (where grade >= 70 and grade < 80) as grade_between_70_and_80
FROM
   grades
127
yieldsfalsehood

別の方法:

SELECT 
    sum(CASE WHEN grade < 70 THEN 1 else 0 END) as grade_less_than_70,
    sum(CASE WHEN grade >= 70 and grade < 80 THEN 1 else 0 END) as grade_between_70_and_80
FROM
   grades

カテゴリカル列でカウントをグループ化する場合にうまく機能します。

11
user1509107

@yieldsfalsehoodによって与えられるソリューションは完全に機能します。

_SELECT
    count(*) filter (where grade < 70) as grade_less_than_70,
    count(*) filter (where grade >= 70 and grade < 80) as grade_between_70_and_80
FROM
    grades
_

ただし、NULLIF(value1, value2)について説明したので、nullifを使用して同じ結果を得る方法があります。

select count(nullif(grade < 70 ,true)) as failed from grades;

4
mysticfyst

Redshiftのみ

怠け者の場合、@ user1509107の答えの上に構築された「COUNTIF」合計整数キャストバージョンは次のとおりです。

SELECT 
    SUM((grade < 70)::INT) AS grade_less_than_70,
    SUM((grade >= 70 AND grade < 80)::INT) AS grade_between_70_and_80
FROM
   grades
1