MySql:多个左连接提供错误的输出

时间:2012-05-03 12:45:05

标签: 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表与'files'表有一对一的关系(这就是为什么我使用'Distinct'),而kc_data和kpo_data数据与'files'表有一对多的关系。 (对于一个files.id,kc_data和kpo_data可以有10到20行)。此查询工作正常。

当我使用另一个一对多表pd_markings(对一个files.id可以有100行)添加另一个左连接时,会出现问题。

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值。实际上这是理想的结果,因为一对一关系表对任何files.id只有1或0行,所以如果我不使用'Distinct'那么结果值是错误的(我猜是因为其他表对同一个文件返回多行一行.id)不幸的是,我的表没有自己的唯一ID列,除了'files'表。

2 个答案:

答案 0 :(得分:4)

您需要flatten the results查询,才能获得正确的计数。

你说你从文件表到其他表有一对多的关系

如果SQL只有关键字LOOKUP而不是在JOIN关键字中填写所有内容,那么很容易推断表A和表B之间的关系是否是一对一的,使用{ {1}}会自动表示一对多。我离题了。无论如何,我应该已经推断出你的文件与dm_data是一对多的;而且,针对kc_data的文件也是一对多的。 JOIN是另一个暗示第一个表和第二个表之间的关系是一对多的;但这并不是决定性的,有些程序员只是用LEFT JOIN编写所有内容。您的查询中的LEFT JOIN没有任何问题,但如果查询中有多个一对多表,那肯定会失败,您的查询将产生针对其他行的重复行。

LEFT JOIN

因此,根据这些知识,您指出文件与dm_data是一对多的,并且对kc_data也是一对多。我们可以得出结论,将这些连接链接起来并将它们分组到一个单一的查询中会出现问题。

一个例子,如果你有三个表,即app(文件),ios_app(dm_data),android_app(kc_data),这就是ios的数据:

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

这是你的android的数据:

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)

如果您只是使用此查询:

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

您可以将链式连接视为笛卡尔积,因此如果第一个表上有3行,第二个表上有2行,则输出为6

这是可视化,看到每个ios AB有2个重复的android AB。有3个ios AB,那么当你做COUNT(ios_app.date_released)时会有什么计数?那将成为6;与 app_code | ios_release_count | android_release_count ----------+-------------------+----------------------- AB | 6 | 6 MK | 0 | 1 PM | 0 | 0 TR | 8 | 8 (4 rows) 相同,这也是6.同样,每个ios TR有4个重复的android TR,ios中有2个TR,所以这将给我们8个数。

COUNT(android_app.date_released)

因此,在将每个结果加入其他表和查询之前,应该将每个结果展平。

如果您的数据库能够进行CTE,请使用。它非常整洁,非常自我记录:

.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功能,比如MySQL,你应该这样做:

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样式的查询将显示正确的输出:

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

实时测试

查询不正确:http://www.sqlfiddle.com/#!2/9774a/2

正确查询:http://www.sqlfiddle.com/#!2/9774a/1

答案 1 :(得分:0)

我在这里质疑你的不同用法 - 它的编写方式将返回1或0.这意味着count distinct只会返回0,1或2。

我假设您在每个表中都有唯一的ID列。您可以更改大小写以返回ID值,然后计算不同的值。如果您的联接返回pd_markings表中同一行的多个,则ID上的非重复计数将返回,而且只返回非常多的行数。