sql >> Database >  >> RDS >> Mysql

MySql:Multiple Left Join geeft verkeerde output

U moet de resultaten afvlakken van uw vraag, om een ​​juiste telling te verkrijgen.

Je zei dat je een een-op-veel-relatie hebt van je bestandentabel naar andere tafel(s)

Als SQL alleen een trefwoord heeft LOOKUP in plaats van alles in JOIN te proppen trefwoorden, moet het gemakkelijk zijn om af te leiden of de relatie tussen tabel A en tabel B één-op-één is, met behulp van JOIN zal automatisch een-op-veel betekenen. Ik dwaal af. Hoe dan ook, ik had al moeten concluderen dat je bestanden een-op-veel zijn tegen dm_data; en ook de bestanden tegen kc_data zijn ook één-op-veel. LEFT JOIN is nog een hint dat de relatie tussen de eerste tabel en de tweede tabel een-op-veel is; dit is echter niet definitief, sommige programmeurs schrijven alles gewoon met LEFT JOIN . Er is niets mis met uw LEFT JOIN in uw zoekopdracht, maar als er meerdere een-op-veel-tabellen in uw zoekopdracht zijn, zal dat zeker mislukken, uw zoekopdracht zal herhalende rijen produceren tegen andere rijen.

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

Dus met deze wetenschap dat je aangeeft dat bestanden één-op-veel zijn tegen dm_data, en het is ook één-op-veel tegen kc_data. We kunnen concluderen dat er iets mis is met het koppelen van die joins en het groeperen ervan op één monolithische query.

Een voorbeeld als je drie tabellen hebt, namelijk app(files), ios_app(dm_data), android_app(kc_data), en dit zijn bijvoorbeeld de data voor 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)

En dit zijn de gegevens voor je 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)    

Als u alleen deze zoekopdracht gebruikt:

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

De uitvoer zal in plaats daarvan verkeerd zijn:

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

Je kunt geketende joins zien als een cartesiaans product, dus als je 3 rijen op de eerste tafel hebt en 2 rijen op de tweede tafel, is de output 6

Hier is de visualisatie, zie dat er 2 herhalende Android AB is voor elke ios AB. Er zijn 3 ios AB, dus wat is het aantal als je COUNT(ios_app.date_released) doet? Dat wordt 6; hetzelfde met COUNT(android_app.date_released) , dit wordt ook 6. Evenzo zijn er 4 herhalende Android TR voor elke ios TR, er zijn 2 TR in ios, dus dat zou ons een telling van 8 geven.

.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)

Dus wat u moet doen, is elk resultaat afvlakken voordat u ze samenvoegt met andere tabellen en query's.

Als uw database CTE aankan, gebruik deze dan. Het is erg netjes en zeer zelfdocumenterend:

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;

Terwijl als uw database nog geen CTE-mogelijkheden heeft, zoals MySQL, u dit in plaats daarvan zou moeten doen:

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

Die zoekopdracht en de zoekopdracht in CTE-stijl zullen de juiste uitvoer tonen:

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

Live-test

Onjuiste zoekopdracht:http://www.sqlfiddle.com/#!2/9774a/ 2

Correcte zoekopdracht:http://www.sqlfiddle.com/#!2/9774a/ 1



  1. Hoe voorkom je dat PDO een vraagteken als een tijdelijke aanduiding interpreteert?

  2. Hoe een-op-een, een-op-veel en veel-op-veel relaties te implementeren tijdens het ontwerpen van tabellen?

  3. MySQL, selecteer records op basis van waarden in JSON-array

  4. Kunnen gelijktijdige transacties met elkaar interfereren? php/mysql