2014-01-22 9 views
27

Ich versuche, eine Abfrage zu schreiben, die nur die Zeilen zählt, die eine Bedingung erfüllen.Wie können in Redshift/Postgres Zeilen gezählt werden, die eine Bedingung erfüllen?

Zum Beispiel in MySQL ich es so schreiben würde:

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

Allerdings, wenn ich versuche, dass auf Redshift zu tun, es gibt die folgende Fehlermeldung:

ERROR: Funktion if (boolean , Integer, "unbekannt") existiert nicht

Hinweis: Keine Funktion entspricht dem angegebenen Namen und den angegebenen Argumenttypen. Sie müssen möglicherweise explizite Typumwandlungen hinzufügen.

überprüfte ich die Dokumentation für bedingte Anweisungen, und ich fand

NULLIF(value1, value2)

aber es vergleicht nur Wert1 und Wert2 und wenn diese Werte gleich sind, gibt es null.

Ich konnte keine einfache IF-Anweisung finden, und auf den ersten Blick konnte ich keinen Weg finden, zu tun, was ich tun möchte.

Ich versuchte, den CASE-Ausdruck zu verwenden, aber ich bin nicht immer die Ergebnisse Ich möchte:

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

Dies ist der Weg, ich will die Dinge zählen:

  • gescheitert (Grad < 70)

  • Durchschnitt (70 < = Grad < 80)

  • gut (80 < = Grad < 90)

  • ausgezeichnet (90 < = Grad < = 100)

und das ist, wie ich erwarte, dass die Ergebnisse sehen:

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

aber ich bekomme das:

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

Ich hoffe, jemand könnte mich in die richtige Richtung zeigen!

Wenn dies hier hilft einige Beispiel-info

CREATE TABLE grades(
    grade integer DEFAULT 0, 
); 

INSERT INTO grades(grade) VALUES(69, 50, 55, 60, 75, 70, 87, 100, 100, 98, 94); 
+0

Vielleicht möchten Sie Betreiber BETWEEN? http://www.postgresqltutorial.com/postgresql-between/ –

Antwort

80

Zuerst wird das Problem, das Sie hier zu haben sind, ist, dass, was Sie sagen ist: „Wenn der Grad weniger als 70, wird der Wert dieses Falles Ausdruck ist count (rank). Ansonsten ist der Wert dieses Ausdrucks count (rank). " In beiden Fällen erhalten Sie also immer den gleichen Wert.

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

count() zählt nur Nicht-Null-Werte, so normalerweise das Muster, das Sie zu erreichen sehen werden, was Sie versuchen dies:

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 

diese Weise wird der Ausdruck Fall nur evaluieren zu 1, wenn der Testausdruck wahr ist und andernfalls Null ist. Dann zählt count() nur die Nicht-Null-Instanzen, d. H. Wenn der Testausdruck wahr ist, was Ihnen geben soll, was Sie brauchen.

Edit: Als Randbemerkung bemerkt, dass dies genau das ist das gleiche wie wie hatte man ursprünglich dieses mit count(if(test, true-value, false-value)) geschrieben, nur neu geschrieben als count(case when test then true-value end) (und null der Stand in falsch-Wert ist, da ein else nicht war an den Fall geliefert).

Bearbeiten: Postgres 9.4 wurde ein paar Monate nach diesem ursprünglichen Austausch veröffentlicht. In dieser Version wurden Aggregatfilter eingeführt, die Szenarien wie diese ein wenig schöner und klarer erscheinen lassen. Diese Antwort wird noch einige gelegentlich upvotes, also wenn Sie auf hier gestolpert und sind eine neuere Postgres verwenden (dh 9.4+) Sie könnten diese gleichwertige Version betrachten wollen:

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 
+0

genial! Genau das habe ich gebraucht. Obwohl ich ein bisschen komisch finde, dass postgres keine einfache IF-Anweisung bietet. Ich denke, CASE..WHEN..ELSE macht die gleiche Arbeit, aber immer noch. IF ist fast Industriestandard. – ILikeTacos

+6

Fall ist der offizielle Standard;) Die Funktion if() ist MySQL, das ist iif() in SQL Server. – yieldsfalsehood

+3

Awesome, danke für die Erwähnung von filter() – Casey

8

Eine andere Methode:

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 

Funktioniert ganz gut, wenn Sie die Anzahl durch eine kategorische Spalte gruppieren möchten.

Verwandte Themen