Mysql
 sql >> Database >  >> RDS >> Mysql

MySql:Multiple Left Join che fornisce un output errato

Devi appiattire i risultati della tua richiesta, al fine di ottenere un conteggio corretto.

Hai detto di avere una relazione uno-a-molti dalla tabella dei file ad altre tabelle

Se SQL ha solo una parola chiave LOOKUP invece di stipare tutto in JOIN parole chiave, sarà facile dedurre se la relazione tra la tabella A e la tabella B è uno a uno, utilizzando JOIN connota automaticamente uno a molti. Io divago. Ad ogni modo, avrei già dovuto dedurre che i tuoi file sono uno a molti contro dm_data; e inoltre, anche i file contro kc_data sono uno a molti. LEFT JOIN è un altro suggerimento che la relazione tra la prima tabella e la seconda tabella è uno a molti; questo non è definitivo però, alcuni programmatori scrivono semplicemente tutto con LEFT JOIN . Non c'è niente di sbagliato nel tuo LEFT JOIN nella tua query, ma se ci sono più tabelle da uno a molti nella tua query, ciò sicuramente fallirà, la tua query produrrà righe ripetute rispetto ad altre righe.

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

Quindi, con questa consapevolezza che indichi che i file sono uno a molti contro dm_data, ed è uno a molti anche contro kc_data. Possiamo concludere che c'è qualcosa di sbagliato nel concatenare quei join e raggrupparli su una query monolitica.

Un esempio se hai tre tabelle, vale a dire app(files), ios_app(dm_data), android_app(kc_data), e questi sono i dati ad esempio per 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 questi sono i dati per il tuo 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 usi semplicemente questa query:

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

L'output sarà invece sbagliato:

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

Puoi pensare ai join concatenati come un prodotto cartesiano, quindi se hai 3 righe sulla prima tabella e 2 righe sulla seconda tabella, l'output sarà 6

Ecco la visualizzazione, vedi che ci sono 2 AB Android ripetuti per ogni AB ios. Ci sono 3 ios AB, quindi quale sarebbe il conteggio quando esegui COUNT(ios_app.date_released)? Quello diventerà 6; lo stesso con COUNT(android_app.date_released) , anche questo sarà 6. Allo stesso modo ci sono 4 TR Android ripetuti per ogni TR ios, ci sono 2 TR in ios, quindi questo ci darebbe un conteggio di 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)

Quindi quello che dovresti fare è appiattire ogni risultato prima di unirlo ad altre tabelle e query.

Se il tuo database è in grado di supportare CTE, utilizzalo. È molto pulito e molto auto-documentante:

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 che se il tuo database non ha ancora funzionalità CTE, come MySQL, dovresti invece farlo:

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

Quella query e la query in stile CTE mostreranno l'output corretto:

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

Test dal vivo

Query errata:http://www.sqlfiddle.com/#!2/9774a/ 2

Query corretta:http://www.sqlfiddle.com/#!2/9774a/ 1