2012-05-03 37 views
4

我在查詢中使用多個左連接有點麻煩。有些表與左表有一對一的關係,有些有一對多的關係。查詢看起來是這樣的:MySql:多個左連接給出錯誤的輸出

Select 
    files.filename, 
    coalesce(count(distinct case 
       when dm_data.weather like '%clear%' then 1 
        end), 
      0) as clear, 
    coalesce(count(distinct case 
       when dm_data.weather like '%lightRain%' then 1 
        end), 
      0) as lightRain, 
    coalesce(count(case 
       when kc_data.type like '%bicycle%' then 1 
        end), 
      0) as bicycle, 
    coalesce(count(case 
       when kc_data.type like '%bus%' then 1 
        end), 
      0) as bus, 
    coalesce(count(case 
       when kpo_data.movement like '%walking%' then 1 
        end), 
      0) as walking, 
    coalesce(count(case 
       when kpo_data.type like '%pedestrian%' then 1 
        end), 
      0) as pedestrian 
from 
    files 
     left join 
    dm_data ON dm_data.id = files.id 
     left join 
    kc_data ON kc_data.id = files.id 
     left join 
    kpo_data ON kpo_data.id = files.id 
where 
    files.filename in (X, Y, Z, ........) 
group by files.filename; 

這裏,dm_data表中有「文件」表一比一的關係(這就是爲什麼我使用「獨特」),而kc_data和kpo_data數據具有單與「文件」表的多對多關係。 (對於一個files.id,kc_data和kpo_data可以有10到20行)。這個查詢工作正常。

問題出現時,我添加另一個一對多表pd_markings(它可以有100行對一個files.id)的另一個左連接。

Select 
    files.filename, 
    coalesce(count(distinct case 
       when dm_data.weather like '%clear%' then 1 
        end), 
      0) as clear, 
    coalesce(count(distinct case 
       when dm_data.weather like '%lightRain%' then 1 
        end), 
      0) as lightRain, 
    coalesce(count(case 
       when kc_data.type like '%bicycle%' then 1 
        end), 
      0) as bicycle, 
    coalesce(count(case 
       when kc_data.type like '%bus%' then 1 
        end), 
      0) as bus, 
    coalesce(count(case 
       when kpo_data.movement like '%walking%' then 1 
        end), 
      0) as walking, 
    coalesce(count(case 
       when kpo_data.type like '%pedestrian%' then 1 
        end), 
      0) as pedestrian, 
    **coalesce(count(case 
       when pd_markings.movement like '%walking%' then 1 
        end), 
      0) as walking** 
from 
    files 
     left join 
    dm_data ON dm_data.id = files.id 
     left join 
    kc_data ON kc_data.id = files.id 
     left join 
    kpo_data ON kpo_data.id = files.id 
     left join 
    **kpo_data ON pd_markings.id = files.id** 
where 
    files.filename in (X, Y, Z, ........) 
group by files.filename; 

現在所有的值都成爲對方的倍數。有任何想法嗎???

請注意,前兩列返回1或0值。這實際上是理想的結果,因爲一對一關係表只有1或0行對任何文件.id,所以如果我不使用'差異',那麼結果值是錯誤的(我猜是因爲其他表格返回的是同一個file.id中的一行以上)不幸的是,不幸的是,我的表沒有自己唯一的ID列,除了'文件'表。

回答

4

您需要對您的查詢的flatten the results,以獲得正確的計數。

你說你從你的文件表,其他桌一個一對多的關係

如果SQL只,而不是填鴨式在JOIN關鍵字一切關鍵字LOOKUP,應當很容易推斷出,如果表A和表B之間的關係是一對一的,使用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。我們可以得出結論,鏈接這些連接並將它們分組在一個單一的查詢中是有問題的。

一個例子,如果你有三個表,即應用程序(文件),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 

輸出將是wron摹代替:

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

你能想到的連鎖加盟作爲笛卡爾積,因此,如果你有第1工作臺3行,並且對第二個表2行,輸出將是6

這裏的可視化,看到每個ios AB都有2個重複的android AB。有3個ios AB,那麼當您執行COUNT(ios_app.date_released)時,計數是多少?那將會變成6;與COUNT(android_app.date_released)一樣,這也將是6.同樣有4重複的Android TR爲每部iOS TR,有在IOS 2 TR,這樣就會給我們的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

+0

感謝您的好解釋。 – sunsa428

+0

你現在的查詢如何?你能一點一點解決嗎? :-) –

+0

感謝您的好解釋。 不,MySql不支持CTE風格的查詢。所以我會和其他建議一起去。其他人也在其他​​帖子中提出了相同的解決方案,但我試圖避免使用這些「子查詢」左連接,因爲連接表本身包含大量的數據,我認爲這些數據可能會減慢查詢執行速度。但現在看來我現在沒有其他選擇了。 另一件事,我們將如何在這種情況下使用「彙總」? – sunsa428

0

我在這裏問一下不同的用途 - 它是書面的方式,它會返回1或0。這意味着不同的計數將只返回0,1或2

我假設你有唯一的ID列你的每張桌子。您可以更改大小寫以返回ID值,然後對其進行計數。如果您的聯接從您的pd_markings表中返回了多個相同的行,則ID上的唯一計數將返回,而且僅返回不同的行數。

+0

是啊,那是關於1或0返回值true。這就是所希望的結果,因爲一對一關係表只有1或0行對任何文件.id,所以如果我不使用'差異',那麼結果值是錯誤的(我猜是因爲其他表返回多於一行反對同一個文件.id) 不幸的是,我的表除了'文件'表沒有自己的唯一ID列。 – sunsa428