2016-12-22 3 views
1

Lorsque j'utilise le CUBE de PostgreSQL sur une requête avec un OUTER JOIN, j'obtiens une ligne supplémentaire NULL qui ne peut pas être distinguée du résultat "tout combiné" tout-NULL du cube .CUBE + jointure externe = ligne NULL supplémentaire

CREATE TABLE species 
    (id SERIAL PRIMARY KEY, 
    name TEXT); 

CREATE TABLE pet 
    (species_id INTEGER REFERENCES species(id), 
    is_adult BOOLEAN, 
    number  INTEGER) 
; 

INSERT INTO species VALUES 
    (1, 'cat'), (2, 'dog'); 

INSERT INTO pet VALUES 
    (1, true, 3), (1, false, 1), (2, true, 1), (null, true, 2); 

OK, donc il y a 7 animaux au total:

SELECT SUM(number) FROM pet; 
sum 
----- 
    7 
(1 row) 

Maintenant, regardez la ligne totale du cube:

SELECT * FROM (
     SELECT name, is_adult, SUM(number) 
     FROM pet p 
     JOIN species s ON (p.species_id = s.id) 
     GROUP BY CUBE (name, is_adult)) subq 
WHERE name IS NULL 
AND is_adult IS NULL; 

name | is_adult | sum 
------+----------+----- 
     |   | 5 
(1 row) 

5 animaux? Oh, c'est vrai, parce que les animaux de compagnie sans espèce ne sont pas inclus. J'ai besoin d'une jointure externe.

SELECT * FROM (
     SELECT name, is_adult, SUM(number) 
     FROM pet p 
     LEFT OUTER JOIN species s ON (p.species_id = s.id) 
     GROUP BY CUBE (name, is_adult)) subq 
WHERE name IS NULL 
AND is_adult IS NULL; 

name | is_adult | sum 
------+----------+----- 
     |   | 2 
     |   | 7 
(2 rows) 

Mon cube a deux lignes null-nulles; le second est la réponse que je voulais. Je comprends à moitié ce qui se passe ici: les valeurs NULL sont utilisées pour signaler deux choses différentes ("le cube a roulé toutes les valeurs de cette colonne" ou "cette ligne n'a pas d'enfants dans la table de droite") . Je ne sais pas comment le réparer.

Répondre

2

valeurs NULL sont utilisées pour signaler deux choses différentes (« le cube a roulé toutes les valeurs de cette colonne » ou « cette ligne n'a pas d'enfant dans la table du côté droit »).

Afin de distinguer un nul de l'autre null, vous pouvez utiliser la fonction grouping(...), voir tableau 9-55 ici: https://www.postgresql.org/docs/9.6/static/functions-aggregate.html#FUNCTIONS-GROUPING-TABLE

GROUPEMENT (args ...) masque binaire entier indiquant les arguments ne sont pas inclus dans l'ensemble de regroupement en cours

Les opérations de regroupement sont utilisées conjointement avec des ensembles de regroupement (voir la section 7.2.4) pour distinguer les lignes de résultat. Les arguments de l'opération GROUPING ne sont pas réellement évalués, mais ils doivent correspondre exactement aux expressions données dans la clause GROUP BY du niveau de requête associé . Les bits sont affectés avec l'argument le plus à droite étant le bit le moins significatif ; chaque bit vaut 0 si l'expression correspondante est incluse dans les critères de regroupement de l'ensemble de regroupement générant la ligne de résultat, et 1 si ce n'est pas le cas.


name | is_adult | sum 
------+----------+----- 
     |   | 2 
     |   | 7 

le second est la réponse que je voulais.

Essayez ceci:

SELECT name, is_adult, SUM(number) 
FROM pet p 
LEFT OUTER JOIN species s ON (p.species_id = s.id) 
GROUP BY CUBE (name, is_adult) 
HAVING grouping(name,is_adult) = 3 

name |is_adult |sum | 
-----|---------|-----| 
    |   |7 | 

S'il vous plaît examiner également cette requête pour savoir comment fonctionne la fonction grouping:

SELECT name, is_adult, SUM(number), grouping(name,is_adult) 
FROM pet p 
LEFT OUTER JOIN species s ON (p.species_id = s.id) 
GROUP BY CUBE (name, is_adult) 

name |is_adult |sum |grouping | 
-----|---------|----|---------| 
cat |false |1 |0  | 
cat |true  |3 |0  | 
cat |   |4 |1  | 
dog |true  |1 |0  | 
dog |   |1 |1  | 
    |true  |2 |0  | 
    |   |2 |1  | 
    |   |7 |3  | 
    |false |1 |2  | 
    |true  |6 |2  |