Instruction SQL pour inclure des lignes sans correspondance dans une sous-requête corrélée

StackOverflow https://stackoverflow.com/questions/1418728

  •  07-07-2019
  •  | 
  •  

Question

J'ai la sous-requête corrélée ci-dessous. Chaque identifiant (147,148,149) dans la table1 a de nombreux enregistrements dans la table2. L'identifiant 148 n'a cependant aucun enregistrement qui corresponde à la condition de temps dans l'instruction. Par conséquent, il n'est pas inclus dans la sortie. Je veux qu'il soit inclus avec un 0 pour la colonne de compte. Que faut-il changer?

SELECT b.fkid, COUNT(DISTINCT username)
FROM table2 AS b
WHERE
b.fkid IN ( 147,148,149 )
AND time > (SELECT SUBTIME(a.endTime, SEC_TO_TIME( 60*60 )) FROM table1 AS a WHERE a.id = b.fkid)
GROUP BY b.fkid

Cette instruction renvoie:

b.fkid   COUNT(DISTINCT username)
147      41
149      26

Je veux qu'il revienne:

b.fkid   COUNT(DISTINCT username)
147      41
148       0
149      26

D'accord, j'ai la solution. C'est une version modifiée de la réponse de rexem:

SELECT t.fkid,
   IFNULL(nu.num_users, 0)
FROM TABLE_2 t
LEFT JOIN (SELECT t.fkid,
                  COUNT(DISTINCT t.username) 'num_users'
           FROM TABLE_2 t
           JOIN TABLE_1 a ON a.id = t.fkid
                          AND SUBTIME(a.endTime, SEC_TO_TIME( 60*60 )) < t.time
           GROUP BY t.fkid) nu ON nu.fkid = t.fkid
WHERE t.fkid IN (147, 148, 149)
GROUP BY t.fkid, nu.num_users

Changes from rexem's answer:
"SEC_TO_TIME( 60*60 )) = t.time" to "SEC_TO_TIME( 60*60 )) < t.time"
Removed "t.time" in GROUP BY clause of subquery
Était-ce utile?

La solution

Essayez:

   SELECT t.fkid,
          IFNULL(nu.num_users, 0)
     FROM TABLE_2 t
LEFT JOIN (SELECT t.fkid,
                  COUNT(DISTINCT t.username) 'num_users'
             FROM TABLE_2 t
             JOIN TABLE_1 a ON a.id = t.fkid
                           AND SUBTIME(a.endTime, SEC_TO_TIME( 60*60 )) = t.time
         GROUP BY t.fkid) nu ON nu.fkid = t.fkid
   WHERE t.fkid IN (147, 148, 149)
GROUP BY t.fkid, nu.num_users

Autres conseils

Essayez d'utiliser COALESCE pour gérer les scénarios dans lesquels le temps est comparé à l'ensemble vide (par exemple, car il n'y a pas de correspondance entre table1 et table2):

SELECT b.fkid, COUNT(DISTINCT username)
FROM table2 AS b
WHEREb.fkid IN ( 147,148,149 )
AND time > COALESCE((SELECT SUBTIME(a.endTime, SEC_TO_TIME( 60*60 )) FROM table1 AS a WHERE a.id = b.fkid), 0)
GROUP BY b.fkid

COALESCE prend un ensemble de paramètres non lié et, à partir de ce groupe, renverra le premier paramètre non null.

SELECT a.ID, COUNT(DISTINCT username)
FROM table1 AS a LEFT JOIN table2 AS b
on a.ID = b.fkID
WHERE a.ID IN ( 147,148,149 )
AND b.time > SUBTIME(a.endTime, SEC_TO_TIME( 60*60 )) 
GROUP BY a.ID

Est-ce que cela aide de toute façon?

EDIT: Je ne suis pas sûr si cela peut fonctionner dans mysql. Mais j'espère que vous aurez l'idée de construire la requête en utilisant ceci.

Je pense que votre meilleure chance d’obtenir le zéro dans le résultat est d’utiliser un UNION (disjoint). La première partie serait votre requête actuelle; la deuxième clause trouverait les valeurs pour lesquelles il n'y a pas d'entrées pertinentes, en sélectionnant la constante 0 pour la colonne count.

Souvent, une jointure externe ferait l'affaire. La difficulté ici est que la jointure externe produirait une ligne de données pour ID = 148, et donc COUNT donnerait une réponse de 1, pas 0.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top