sql >> Databasteknik >  >> RDS >> PostgreSQL

GROUP BY + CASE uttalande

Din fråga skulle redan fungera - förutom att du stöter på namnkonflikter eller bara förvirrar utdatakolumnen (CASE uttryck) med källkolumnen result , som har annat innehåll.

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

Du måste GROUP BY ditt CASE uttryck istället för din källkolumn:

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

Eller ange ett kolumnalias som skiljer sig från alla kolumner i FROM lista - annars har den kolumnen företräde:

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

SQL-standarden är ganska speciell i detta avseende. Citerar manualen här:

En utdatakolumns namn kan användas för att referera till kolumnens värde iORDER BY och GROUP BY satser, men inte i WHERE eller HAVING klausuler;där måste du skriva ut uttrycket istället.

Och:

Om en ORDER BY expression är ett enkelt namn som matchar både ett utdatakolumnnamn och ett indatakolumnnamn, ORDER BY kommer att tolka det som utdatakolumnnamnet. Detta är motsatsen till valet som GROUP BY kommer att göra i samma situation. Denna inkonsekvens görs för att vara kompatibel med SQL-standarden.

Fet betoning min.

Dessa konflikter kan undvikas genom att använda positionsreferenser (ordningstal) i GROUP BY och ORDER BY , hänvisar till objekt i SELECT lista från vänster till höger. Se lösningen nedan.
Nackdelen är att detta kan vara svårare att läsa och sårbart för redigeringar i SELECT lista (man kan glömma att anpassa positionsreferenser därefter).

Men det gör du inte måste lägga till kolumnen day till GROUP BY sats, så länge den har ett konstant värde (CURRENT_DATE-1 ).

Omskrivet och förenklat med korrekt JOIN-syntax och positionsreferenser kan det se ut så här:

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;

Observera också att jag undviker kolumnnamnet time . Det är ett reserverat ord och bör aldrig användas som identifierare. Dessutom är din "tid" uppenbarligen en timestamp eller date , så det är ganska missvisande.



  1. Använd FILEGROUP_ID() för att returnera en filgrupps ID i SQL Server

  2. Kommer ett SQL Server Job att hoppa över en schemalagd körning om den redan körs?

  3. Lägg till ny RAC-instans manuellt

  4. Vad är det bästa sättet att visa en bild från en sql-serverdatabas i asp.net?