MySQL - 列的条件连接

时间:2014-11-03 19:51:13

标签: mysql sql join optimization

我有以下查询:

select ad_st_id_state, count(distinct id_visit) as Visits
from sf_visit
join vr_users on vi_us_id_user = sus_us_id_user
join sf_pdv on vi_pdv_id_pdv = id_pdv
join sf_address on pdv_ad_id_address = id_address

group by ad_st_id_state
order by ad_st_id_state

我也有这个:

select ad_st_id_state, count(distinct id_visit) as DoneVisits
from sf_visit
join vr_users on vi_us_id_user = sus_us_id_user
join sf_pdv on vi_pdv_id_pdv = id_pdv
join sf_address on pdv_ad_id_address = id_address
join sf_visit_file_time on id_visit = vft_vi_id_visit /* Another join has been added */

group by ad_st_id_state
order by ad_st_id_state

正如您所看到的,除了额外的join语句之外,查询几乎是相同的。 两个查询都返回了我需要的正确值,但我需要将它们连接到一个表中,所以我这样做:

select fffuuu.ad_st_id_state, count(distinct id_visit) as Visitas, fffuuu.doneVisits
from sf_visit
join vr_users on vi_us_id_user = sus_us_id_user
join sf_pdv on vi_pdv_id_pdv = id_pdv
join sf_address on pdv_ad_id_address = id_address

join (
select ad_st_id_state, count(distinct id_visit) as doneVisits
from sf_visit
join vr_users on vi_us_id_user = sus_us_id_user
join sf_pdv on vi_pdv_id_pdv = id_pdv
join sf_address on pdv_ad_id_address = id_address
join sf_visit_file_time on id_visit = vft_vi_id_visit

group by ad_st_id_state
order by ad_st_id_state
) as fffuuu on sf_address.ad_st_id_state = fffuuu.ad_st_id_state

group by ad_st_id_state
order by ad_st_id_state

或者换句话说,我将第一个查询与第二个查询作为子查询加入。结果集很好并且正确但是花费的时间太长,所以我在另一个系统中运行此查询正在运行超时。 每个查询都独立运行,但加入它们对我的需求来说太慢了......

我想知道是否有一种优化方法,我在想是否有一些连接条件语句或其他东西。我搜索了信息,但我没有运气。 我想的是:

select ad_st_id_state, count(distinct id_visit) as Visits, if(@someVariable := true) as DoneVisits
from sf_visit
join vr_users on vi_us_id_user = sus_us_id_user
join sf_pdv on vi_pdv_id_pdv = id_pdv
join sf_address on pdv_ad_id_address = id_address
if (@someVariable == true) then join sf_visit_file_time on id_visit = vft_vi_id_visit

group by ad_st_id_state
order by ad_st_id_state

或类似的东西。有人可以帮我吗?我该如何优化呢? 致谢

4 个答案:

答案 0 :(得分:4)

您可以在计数中使用外部联接sf_visit_file_time和case语句吗?显然,我没有在本地拥有你的架构,但是像:

select ad_st_id_state, 
   count(distinct id_visit) as Visits,
   count(distinct case when vft_vi_id_visit is not null then id_visit end) as DoneVisits
from sf_visit
join vr_users on vi_us_id_user = sus_us_id_user
join sf_pdv on vi_pdv_id_pdv = id_pdv
join sf_address on pdv_ad_id_address = id_address
left join sf_visit_file_time on id_visit = vft_vi_id_visit
group by ad_st_id_state
order by ad_st_id_state

答案 1 :(得分:1)

如果两个查询都很快并且运行良好而您只需要在一个表中使用结果,则可以使用联合选择http://dev.mysql.com/doc/refman/5.0/en/union.html

select ad_st_id_state, count(distinct id_visit) as Visits, '' as DoneVisits
from sf_visit
join vr_users on vi_us_id_user = sus_us_id_user
join sf_pdv on vi_pdv_id_pdv = id_pdv
join sf_address on pdv_ad_id_address = id_address
group by ad_st_id_state
UNION
select ad_st_id_state, '' as Visits, count(distinct id_visit) as DoneVisits
from sf_visit
join vr_users on vi_us_id_user = sus_us_id_user
join sf_pdv on vi_pdv_id_pdv = id_pdv
join sf_address on pdv_ad_id_address = id_address
join sf_visit_file_time on id_visit = vft_vi_id_visit /* Another join has been added */
group by ad_st_id_state
order by ad_st_id_state

答案 2 :(得分:1)

您的查询都干净而有效。因此,只需将它们连接在一起进行演示,将它们作为子查询处理。

SELECT a.ad_st_id_state, a.Visits, b.DoneVisits
  FROM (
        /* put your first query here */
       ) AS a
  LEFT JOIN (
        /* put your second query here */
       ) AS b ON a.ad_st_id_state = b.ad_st_id_state
 ORDER BY a.ad_st_id_state

这会产生更大的查询,但它应该以您拥有的两个查询的时间总和运行。您可以将ORDER BY子句从子查询中删除。

所以它看起来像是一个真正的查询俱乐部三明治。

SELECT a.ad_st_id_state, a.Visits, b.DoneVisits
  FROM (
          select ad_st_id_state, count(distinct id_visit) as Visits
            from sf_visit
            join vr_users on vi_us_id_user = sus_us_id_user
            join sf_pdv on vi_pdv_id_pdv = id_pdv
            join sf_address on pdv_ad_id_address = id_address
           group by ad_st_id_state
       ) AS a
  LEFT JOIN (
           select ad_st_id_state, count(distinct id_visit) as DoneVisits
            from sf_visit
            join vr_users on vi_us_id_user = sus_us_id_user
            join sf_pdv on vi_pdv_id_pdv = id_pdv
            join sf_address on pdv_ad_id_address = id_address
            join sf_visit_file_time on id_visit = vft_vi_id_visit /* Another join */
           group by ad_st_id_state
       ) AS b ON a.ad_st_id_state = b.ad_st_id_state
 ORDER BY a.ad_st_id_state

答案 3 :(得分:0)

在这种情况下,你是“条件联接”告诉你“如果我在另一张表中找到记录,则表示访问已完成。”

为了创建“条件”,您可以使用LEFT OUTER JOIN代替INNER JOIN

如果在一个表中找不到记录而不在另一个表中找到记录,则OUTER JOIN而不是INNER JOIN不会中断。它仍然返回记录。有LEFTRIGHT外部联接。你应该自己研究一下。但基本上,为了实现你想要的,你可以这样做:

select ad_st_id_state, count(distinct vft_vi_id_visit) as DoneVisits
from sf_visit
join vr_users on vi_us_id_user = sus_us_id_user
join sf_pdv on vi_pdv_id_pdv = id_pdv
join sf_address on pdv_ad_id_address = id_address
left outer join sf_visit_file_time on id_visit = vft_vi_id_visit /* Another join has been added */
group by ad_st_id_state
order by ad_st_id_state

所以现在,DoneVisits将永远存在 - 当没有sf_visit_file_time记录时它将只为零。