2012-05-03 40 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