sql >> Database >  >> RDS >> PostgreSQL

GROUP BY + CASE-instructie

Uw zoekopdracht zou al werken, behalve dat u naamconflicten tegenkomt of gewoon de uitvoerkolom verwart (de CASE expressie) met bronkolom result , die andere inhoud heeft.

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

U moet GROUP BY uw CASE expressie in plaats van uw bronkolom:

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

Of geef een kolomalias dat is anders dan elke kolomnaam in de FROM lijst - anders heeft die kolom voorrang:

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

De SQL-standaard is in dit opzicht nogal eigenaardig. Ik citeer hier de handleiding:

De naam van een uitvoerkolom kan worden gebruikt om te verwijzen naar de waarde van de kolom inORDER BY en GROUP BY clausules, maar niet in de WHERE of HAVING clausules; daar moet u in plaats daarvan de uitdrukking uitschrijven.

En:

Als een ORDER BY expressie is een eenvoudige naam die overeenkomt met zowel een uitvoerkolomnaam als een invoerkolomnaam, ORDER BY zal het interpreteren als de naam van de uitvoerkolom. Dit is het tegenovergestelde van de keuze die GROUP BY zal maken in dezelfde situatie. Deze inconsistentie is gemaakt om compatibel te zijn met de SQL-standaard.

Vet benadruk de mijne.

Deze conflicten kunnen worden vermeden door gebruik te maken van positionele verwijzingen (volgnummers) in GROUP BY en ORDER BY , verwijzend naar items in de SELECT lijst van links naar rechts. Zie de oplossing hieronder.
Het nadeel is dat dit moeilijker te lezen en kwetsbaarder kan zijn voor bewerkingen in de SELECT lijst (men zou kunnen vergeten positionele referenties dienovereenkomstig aan te passen).

Maar jij niet moet de kolom day . toevoegen naar de GROUP BY clausule, zolang deze een constante waarde heeft (CURRENT_DATE-1 ).

Herschreven en vereenvoudigd met de juiste JOIN-syntaxis en positionele verwijzingen zou het er als volgt uit kunnen zien:

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;

Merk ook op dat ik de kolomnaam time vermijd . Dat is een gereserveerd woord en mag nooit als identifier worden gebruikt. Trouwens, je "tijd" is duidelijk een timestamp of date , dus dat is nogal misleidend.



  1. EF5:Kan het bestand '{0}' niet toevoegen als database '{1}'

  2. Zoek gebroken objecten in SQL Server

  3. PostgreSQL retourneert een functie met een aangepast gegevenstype

  4. Voordelen van NoSQL-databases – Alles wat u moet weten