Mysql
 sql >> Base de données >  >> RDS >> Mysql

MySql:Multiple Left Join donnant une mauvaise sortie

Vous devez aplatir les résultats de votre requête, afin d'obtenir un décompte juste.

Vous avez dit que vous avez une relation un-à-plusieurs entre votre table de fichiers et d'autres tables

Si SQL n'a qu'un mot-clé LOOKUP au lieu de tout caser dans JOIN mots-clés, il doit être facile de déduire si la relation entre la table A et la table B est un-à-un, en utilisant JOIN connote automatiquement un-à-plusieurs. Je digresse. Quoi qu'il en soit, j'aurais déjà dû en déduire que vos fichiers sont un à plusieurs contre dm_data; et aussi, les fichiers contre kc_data sont aussi un à plusieurs. LEFT JOIN est un autre indice que la relation entre la première table et la deuxième table est un-à-plusieurs ; ce n'est pas définitif cependant, certains codeurs écrivent simplement tout avec LEFT JOIN . Il n'y a rien de mal avec votre LEFT JOIN dans votre requête, mais s'il y a plusieurs tables un-à-plusieurs dans votre requête, cela échouera sûrement, votre requête produira des lignes répétées par rapport à d'autres lignes.

from
    files
        left join
    dm_data ON dm_data.id = files.id
        left join
    kc_data ON kc_data.id = files.id

Donc, avec cette connaissance, vous indiquez que les fichiers sont un à plusieurs contre dm_data, et c'est aussi un à plusieurs contre kc_data. Nous pouvons en conclure qu'il y a quelque chose de mal à enchaîner ces jointures et à les regrouper sur une requête monolithique.

Un exemple si vous avez trois tables, à savoir app(files), ios_app(dm_data), android_app(kc_data), et voici les données par exemple pour ios :

test=# select * from ios_app order by app_code, date_released;
 ios_app_id | app_code | date_released | price  
------------+----------+---------------+--------
          1 | AB       | 2010-01-01    | 1.0000
          3 | AB       | 2010-01-03    | 3.0000
          4 | AB       | 2010-01-04    | 4.0000
          2 | TR       | 2010-01-02    | 2.0000
          5 | TR       | 2010-01-05    | 5.0000
(5 rows)

Et voici les données pour votre Android :

test=# select * from android_app order by app_code, date_released;
.android_app_id | app_code | date_released |  price  
----------------+----------+---------------+---------
              1 | AB       | 2010-01-06    |  6.0000
              2 | AB       | 2010-01-07    |  7.0000
              7 | MK       | 2010-01-07    |  7.0000
              3 | TR       | 2010-01-08    |  8.0000
              4 | TR       | 2010-01-09    |  9.0000
              5 | TR       | 2010-01-10    | 10.0000
              6 | TR       | 2010-01-11    | 11.0000
(7 rows)    

Si vous utilisez simplement cette requête :

select x.app_code, 
    count(i.date_released) as ios_release_count, 
    count(a.date_released) as android_release_count
from app x
left join ios_app i on i.app_code = x.app_code
left join android_app a on a.app_code = x.app_code
group by x.app_code
order by x.app_code

La sortie sera erronée :

 app_code | ios_release_count | android_release_count 
----------+-------------------+-----------------------
 AB       |                 6 |                     6
 MK       |                 0 |                     1
 PM       |                 0 |                     0
 TR       |                 8 |                     8
(4 rows)

Vous pouvez considérer les jointures chaînées comme un produit cartésien, donc si vous avez 3 lignes sur la première table et 2 lignes sur la deuxième table, la sortie sera 6

Voici la visualisation, voyez qu'il y a 2 répétitions Android AB pour chaque ios AB. Il y a 3 ios AB, alors quel serait le décompte lorsque vous feriez COUNT(ios_app.date_released) ? Cela deviendra 6; pareil avec COUNT(android_app.date_released) , ce sera également 6. De même, il y a 4 TR android répétés pour chaque TR ios, il y a 2 TR dans ios, donc cela nous donnerait un compte de 8.

.app_code | ios_release_date | android_release_date 
----------+------------------+----------------------
 AB       | 2010-01-01       | 2010-01-06
 AB       | 2010-01-01       | 2010-01-07
 AB       | 2010-01-03       | 2010-01-06
 AB       | 2010-01-03       | 2010-01-07
 AB       | 2010-01-04       | 2010-01-06
 AB       | 2010-01-04       | 2010-01-07
 MK       |                  | 2010-01-07
 PM       |                  | 
 TR       | 2010-01-02       | 2010-01-08
 TR       | 2010-01-02       | 2010-01-09
 TR       | 2010-01-02       | 2010-01-10
 TR       | 2010-01-02       | 2010-01-11
 TR       | 2010-01-05       | 2010-01-08
 TR       | 2010-01-05       | 2010-01-09
 TR       | 2010-01-05       | 2010-01-10
 TR       | 2010-01-05       | 2010-01-11
(16 rows)

Donc, ce que vous devez faire est d'aplatir chaque résultat avant de les joindre à d'autres tables et requêtes.

Si votre base de données est capable de CTE, veuillez l'utiliser. C'est très soigné et très auto-documenté :

with ios_app_release_count_list as
(
 select app_code, count(date_released) as ios_release_count
 from ios_app
 group by app_code
)
,android_release_count_list as
(
 select app_code, count(date_released) as android_release_count 
 from android_app 
 group by app_code  
)
select
 x.app_code, 
 coalesce(i.ios_release_count,0) as ios_release_count, 
 coalesce(a.android_release_count,0) as android_release_count
from app x
left join ios_app_release_count_list i on i.app_code = x.app_code
left join android_release_count_list a on a.app_code = x.app_code
order by x.app_code;

Alors que si votre base de données n'a pas encore de capacité CTE, comme MySQL, vous devriez faire ceci à la place :

select x.app_code, 
 coalesce(i.ios_release_count,0) as ios_release_count, 
 coalesce(a.android_release_count,0) as android_release_count
from app x
left join
(
 select app_code, count(date_released) as ios_release_count
 from ios_app
 group by app_code
) i on i.app_code = x.app_code
left join
(
 select app_code, count(date_released) as android_release_count 
 from android_app 
 group by app_code   
) a on a.app_code = x.app_code
order by x.app_code

Cette requête et la requête de style CTE afficheront le résultat correct :

 app_code | ios_release_count | android_release_count 
----------+-------------------+-----------------------
 AB       |                 3 |                     2
 MK       |                 0 |                     1
 PM       |                 0 |                     0
 TR       |                 2 |                     4
(4 rows)

Test en direct

Requête incorrecte :http://www.sqlfiddle.com/#!2/9774a/ 2

Requête correcte :http://www.sqlfiddle.com/#!2/9774a/ 1