Mysql
 sql >> база данни >  >> RDS >> Mysql

MySql:Множество ляво присъединяване дава грешен изход

Трябва да изравнявате резултатите от вашата заявка, за да получите точен брой.

Казахте, че имате връзка едно към много от вашата таблица с файлове към друга(и) таблица(и)

Ако SQL има само ключова дума LOOKUP вместо да тъпчем всичко в JOIN ключови думи, ще бъде лесно да се заключи дали връзката между таблица А и таблица Б е едно към едно, като се използва JOIN автоматично ще означава едно към много. отклонявам се. Както и да е, вече трябваше да заключих, че вашите файлове са едно към много срещу dm_data; а също и файловете срещу kc_data също са едно към много. LEFT JOIN е друг намек, че връзката между първата таблица и втората таблица е едно към много; това обаче не е окончателно, някои кодери просто пишат всичко с LEFT JOIN . Няма нищо лошо във вашето LEFT JOIN във вашата заявка, но ако в заявката ви има множество таблици една към много, това със сигурност ще се провали, вашата заявка ще доведе до повтарящи се редове срещу други редове.

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

Така че с това знание, че посочвате, че файловете са един към много срещу dm_data, и е един към много също срещу kc_data. Можем да заключим, че има нещо нередно с верижното свързване и групирането им в една монолитна заявка.

Пример, ако имате три таблици, а именно app(files), ios_app(dm_data), android_app(kc_data) и това са данните например за 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)

И това са данните за вашия 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)    

Ако използвате само тази заявка:

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

Вместо това изходът ще бъде грешен:

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

Можете да мислите за верижните съединения като декартово произведение, така че ако имате 3 реда на първата таблица и 2 реда на втората таблица, изходът ще бъде 6

Ето визуализацията, вижте, че има 2 повтарящи се android AB за всеки ios AB. Има 3 ios AB, така че какъв ще бъде броят, когато направите COUNT(ios_app.date_released)? Това ще стане 6; същото с COUNT(android_app.date_released) , това също ще бъде 6. По същия начин има 4 повтарящи се android TR за всеки ios TR, има 2 TR в ios, така че това ще ни даде 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)

Така че това, което трябва да направите, е да изгладите всеки резултат, преди да ги присъедините към други таблици и заявки.

Ако вашата база данни е способна на CTE, моля, използвайте го. Много е спретнат и много самодокументиращ:

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;

Като има предвид, че ако вашата база данни все още няма възможност за CTE, като MySQL, трябва да направите това вместо това:

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

Тази заявка и заявката в стил CTE ще покажат правилния изход:

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

Тест на живо

Неправилна заявка:http://www.sqlfiddle.com/#!2/9774a/ 2

Правилна заявка:http://www.sqlfiddle.com/#!2/9774a/ 1



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Не мога да осъществя JDBC връзка с MySQL (използвайки Java, IntelliJ и Linux)

  2. Django MySQL отделна заявка за получаване на множество стойности

  3. Как да изберете един ред на случаен принцип, като се вземе предвид теглото?

  4. Незаконен микс от съпоставяне (utf8_unicode_ci,IMPLICIT) и (utf8_general_ci,IMPLICIT) за операция '='

  5. Как да накарам заявка да не връща нищо, когато няма условия?