web-dev-qa-db-fra.com

Instruction GROUP BY + CASE

J'ai une requête de travail qui regroupe des données par modèle matériel et un résultat, mais le problème est qu'il existe de nombreux "résultats" . J'ai essayé de réduire cela à "si result = 0 alors garder à 0, sinon mettez le à 1" . Cela fonctionne généralement, mais je finis par avoir:

    day     |      name      | type | case | count
------------+----------------+------+------+-------
 2013-11-06 | modelA         |    1 |    0 |   972
 2013-11-06 | modelA         |    1 |    1 |    42
 2013-11-06 | modelA         |    1 |    1 |     2
 2013-11-06 | modelA         |    1 |    1 |    11
 2013-11-06 | modelB         |    1 |    0 |   456
 2013-11-06 | modelB         |    1 |    1 |    16
 2013-11-06 | modelB         |    1 |    1 |     8
 2013-11-06 | modelB         |    3 |    0 | 21518
 2013-11-06 | modelB         |    3 |    1 |     5
 2013-11-06 | modelB         |    3 |    1 |     7
 2013-11-06 | modelB         |    3 |    1 |   563

Au lieu de l'agrégat que j'essaie d'atteindre, il ne contient qu'une ligne par combinaison type/cas.

    day     |      name      | type | case | count
------------+----------------+------+------+-------
 2013-11-06 | modelA         |    1 |    0 |   972
 2013-11-06 | modelA         |    1 |    1 |    55
 2013-11-06 | modelB         |    1 |    0 |   456
 2013-11-06 | modelB         |    1 |    1 |    24
 2013-11-06 | modelB         |    3 |    0 | 21518
 2013-11-06 | modelB         |    3 |    1 |   575

Voici ma requête:

select CURRENT_DATE-1 AS day, model.name, attempt.type, 
       CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END, 
       count(*) 
from attempt attempt, prod_hw_id prod_hw_id, model model
where time >= '2013-11-06 00:00:00'  
AND time < '2013-11-07 00:00:00'
AND attempt.hard_id = prod_hw_id.hard_id
AND prod_hw_id.model_id = model.model_id
group by model.name, attempt.type, attempt.result
order by model.name, attempt.type, attempt.result;

Tous les conseils sur la façon dont je peux atteindre ce serait génial.

Le jour sera toujours défini dans la clause WHERE et ne variera donc pas. name, type, result(case) et count varieront. En bref, pour un modèle donné, je veux seulement 1 ligne par "type + cas" combo. Comme vous pouvez le voir dans le premier jeu de résultats, j'ai 3 lignes pour modelA qui ont type=1 et case=1 (car il y a beaucoup de "résultat" valeurs que j'ai converties en 0 = 0 et rien d'autre = 1 ). Je veux que cela soit représenté comme une ligne avec le nombre agrégé comme dans l'exemple 2.

18
ssbsts

Votre requête fonctionnait déjà - sauf que vous rencontrez des conflits de noms ou que vous confondez la colonne de sortie (l'expression CASE) avec la colonne source} _ result, dont le contenu est différent.

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

Vous devez GROUP BY votre expression CASE à la place de votre colonne source:

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

Vous pouvez également fournir un alias de colonne} différent de tout nom de colonne de la liste FROM. Sinon, cette colonne est prioritaire:

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

Le standard SQL est assez particulier à cet égard. Citer le manuel ici:

Le nom d'une colonne de sortie peut être utilisé pour faire référence à la valeur de la colonne dans clauses ORDER BY et GROUP BY, mais pas dans les clauses WHERE ou HAVING; là vous devez écrire l'expression à la place.

Et:

Si une expression ORDER BY est un nom simple qui correspond à la fois à une sortie nom de colonne et un nom de colonne en entrée, ORDER BY l'interprétera comme le nom de la colonne de sortie. _ {C'est l'inverse du choix que GROUP BY.__ fera.} Dans la même situation. Cette incohérence est faite pour être compatible avec le standard SQL.

Gras l'emphase est la mienne.

Ces conflits peuvent être évités en utilisant références de position (nombres ordinaux) dans GROUP BY et ORDER BY, en référençant les éléments de la liste SELECT de gauche à droite. Voir la solution ci-dessous.
L’inconvénient est que ceci peut être plus difficile à lire et vulnérable aux modifications de la liste SELECT (on pourrait oublier d’adapter les références de position en conséquence). 

Mais vous pas devez ajouter la colonne day à la clause GROUP BY tant qu'elle contient une valeur constante (CURRENT_DATE-1).

Réécrit et simplifié avec la syntaxe JOIN et les références de position appropriées, il pourrait ressembler à ceci:

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;

Notez également que j'évite le nom de colonne time. C'est un mot réservé et ne doit jamais être utilisé comme identifiant. En outre, votre "temps" est évidemment un timestamp OU date , ce qui est plutôt trompeur.

51

pouvez-vous s'il vous plaît essayer ceci: remplacer la déclaration de cas par celle ci-dessous 

Sum(CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END) as Count,
6
Malathi

Essayez d’ajouter les deux autres colonnes non COUNT à GROUP BY:

select CURRENT_DATE-1 AS day, 
model.name, 
attempt.type, 
CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END, 
count(*) 
from attempt attempt, prod_hw_id prod_hw_id, model model
where time >= '2013-11-06 00:00:00'  
AND time < '2013-11-07 00:00:00'
AND attempt.hard_id = prod_hw_id.hard_id
AND prod_hw_id.model_id = model.model_id
group by 1,2,3,4
order by model.name, attempt.type, attempt.result;
1
Filipe Silva

Pour TSQL, j'aime bien encapsuler des instructions case dans une application externe. Cela évite d'avoir à écrire l'instruction case deux fois, permet de faire référence à l'instruction case par alias lors de futures jointures et évite le recours à des références de position.

select oa.day, 
model.name, 
attempt.type, 
oa.result
COUNT(*) MyCount 
FROM attempt attempt, prod_hw_id prod_hw_id, model model
WHERE time >= '2013-11-06 00:00:00'  
AND time < '2013-11-07 00:00:00'
AND attempt.hard_id = prod_hw_id.hard_id
AND prod_hw_id.model_id = model.model_id
OUTER APPLY (
    SELECT CURRENT_DATE-1 AS day,
     CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END result
    ) oa    
group by oa.day, 
model.name, 
attempt.type, 
oa.result
order by model.name, attempt.type, oa.result;
0
Ryan O'Donnell