Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

MySql:Multiple Left Join dando saída errada


Você precisa achatar os resultados de sua consulta, a fim de obter uma contagem correta.

Você disse que tem um relacionamento um-para-muitos da sua tabela de arquivos para outra(s) tabela(s)

Se o SQL tiver apenas uma palavra-chave LOOKUP em vez de encher tudo em JOIN palavras-chave, será fácil inferir se a relação entre a tabela A e a tabela B é um-para-um, usando JOIN automaticamente conota um-para-muitos. Eu discordo. De qualquer forma, eu já deveria ter inferido que seus arquivos são um-para-muitos contra dm_data; e também, os arquivos contra kc_data também são um para muitos. LEFT JOIN é outra dica de que a relação entre a primeira e a segunda tabela é um-para-muitos; isso não é definitivo, porém, alguns codificadores apenas escrevem tudo com LEFT JOIN . Não há nada de errado com seu LEFT JOIN em sua consulta, mas se houver várias tabelas um para muitos em sua consulta, isso certamente falhará, sua consulta produzirá linhas repetidas em outras linhas.
from
    files
        left join
    dm_data ON dm_data.id = files.id
        left join
    kc_data ON kc_data.id = files.id

Portanto, com esse conhecimento, você indica que os arquivos são um-para-muitos contra dm_data e também um-para-muitos contra kc_data. Podemos concluir que há algo errado em encadear essas junções e agrupá-las em uma consulta monolítica.

Um exemplo, se você tiver três tabelas, ou seja, app(files), ios_app(dm_data), android_app(kc_data), e estes são os dados, por exemplo, para 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)

E estes são os dados para o seu 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)    

Se você simplesmente usar esta consulta:
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

A saída estará errada em vez disso:
 app_code | ios_release_count | android_release_count 
----------+-------------------+-----------------------
 AB       |                 6 |                     6
 MK       |                 0 |                     1
 PM       |                 0 |                     0
 TR       |                 8 |                     8
(4 rows)

Você pode pensar em junções encadeadas como produtos cartesianos, portanto, se você tiver 3 linhas na primeira tabela e tiver 2 linhas na segunda tabela, a saída será 6

Aqui está a visualização, veja que há 2 android AB repetidos para cada ios AB. Existem 3 ios AB, então qual seria a contagem quando você fizer COUNT(ios_app.date_released)? Isso se tornará 6; o mesmo com COUNT(android_app.date_released) , isso também será 6. Da mesma forma, há 4 TR de Android repetidos para cada TR de ios, existem 2 TR em ios, então isso nos daria uma contagem 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)

Portanto, o que você deve fazer é nivelar cada resultado antes de juntá-los a outras tabelas e consultas.

Se seu banco de dados for compatível com CTE, use-o. É muito legal e muito auto-documentado:
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;

Considerando que, se o seu banco de dados ainda não tiver capacidade CTE, como o MySQL, você deve fazer isso:
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

Essa consulta e a consulta no estilo CTE mostrarão a saída correta:
 app_code | ios_release_count | android_release_count 
----------+-------------------+-----------------------
 AB       |                 3 |                     2
 MK       |                 0 |                     1
 PM       |                 0 |                     0
 TR       |                 2 |                     4
(4 rows)

Teste ao vivo

Consulta incorreta:http://www.sqlfiddle.com/#!2/9774a/ 2

Consulta correta:http://www.sqlfiddle.com/#!2/9774a/ 1