Трябва да изравнявате резултатите от вашата заявка, за да получите точен брой.
Казахте, че имате връзка едно към много от вашата таблица с файлове към друга(и) таблица(и)
Ако 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