sql >> Database teknologi >  >> RDS >> PostgreSQL

GROUP BY + CASE erklæring

Din forespørgsel ville allerede virke - bortset fra at du løber ind i navnekonflikter eller bare forvirrer outputkolonnen (CASE udtryk) med kildekolonnen result , som har forskelligt indhold.

...
GROUP BY model.name, attempt.type, attempt.result
...

Du skal GROUP BY din CASE udtryk i stedet for din kildekolonne:

...
GROUP BY model.name, attempt.type
       , CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END
...

Eller angiv et kolonnealias det er forskelligt fra et hvilket som helst kolonnenavn i FROM liste - ellers har den kolonne forrang:

SELECT ...
     , CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END AS result1
...
GROUP BY model.name, attempt.type, result1
...

SQL-standarden er ret ejendommelig i denne henseende. Citerer manualen her:

En outputkolonnes navn kan bruges til at henvise til kolonnens værdi iORDER BY og GROUP BY klausuler, men ikke i WHERE eller HAVING klausuler;der skal du skrive udtrykket ud i stedet.

Og:

Hvis en ORDER BY udtryk er et simpelt navn, der matcher både et outputkolonnenavn og et inputkolonnenavn, ORDER BY vil fortolke det som outputkolonnenavnet. Dette er det modsatte af valget GROUP BY vil gøre i samme situation. Denne inkonsistens er lavet for at være kompatibel med SQL-standarden.

Fed vægt min.

Disse konflikter kan undgås ved at bruge positionelle referencer (ordenstal) i GROUP BY og ORDER BY , der henviser til elementer i SELECT liste fra venstre mod højre. Se løsningen nedenfor.
Ulempen er, at dette kan være sværere at læse og sårbart over for redigeringer i SELECT liste (man kan glemme at tilpasse positionsreferencer i overensstemmelse hermed).

Men det gør du ikke skal tilføje kolonnen day til GROUP BY klausul, så længe den har en konstant værdi (CURRENT_DATE-1 ).

Omskrevet og forenklet med korrekt JOIN-syntaks og positionsreferencer kunne det se sådan ud:

SELECT m.name
     , a.type
     , CASE WHEN a.result = 0 THEN 0 ELSE 1 END AS result
     , CURRENT_DATE - 1 AS day
     , count(*) AS ct
FROM   attempt    a
JOIN   prod_hw_id p USING (hard_id)
JOIN   model      m USING (model_id)
WHERE  ts >= '2013-11-06 00:00:00'  
AND    ts <  '2013-11-07 00:00:00'
GROUP  BY 1,2,3
ORDER  BY 1,2,3;

Bemærk også, at jeg undgår kolonnenavnet time . Det er et reserveret ord og bør aldrig bruges som identifikator. Desuden er din "tid" naturligvis et timestamp eller date , så det er ret misvisende.



  1. Sådan fungerer make_time() i PostgreSQL

  2. Kolonnenavnet eller antallet af angivne værdier matcher ikke tabeldefinitionen

  3. MySQL kan ikke tilføje fremmednøglebegrænsning

  4. Sådan ændres prioritet for samtidig program