【问题标题】:MySQL - Conditional Join for a columnMySQL - 列的条件连接
【发布时间】:2023-03-09 10:47:01
【问题描述】:

我有以下疑问:

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

或者类似的东西。我该如何优化它?

【问题讨论】:

  • 以下在 t-sql 中工作,所以我确信它也可以在 mysql 中工作添加一个类似的连接......在 id_visis = vft_vi_id_visit 和 @someVariable = 1 上加入 sf_visit_file_time
  • @SkelDave 我尝试了你的建议,但我不知道该怎么做,因为我在 MySQL 中遇到了语法错误。我刚刚编写了关于 someVariable 的代码,以阐明我想要做什么。

标签: mysql sql join optimization


【解决方案1】:

您能否在计数中使用 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

【讨论】:

  • 够了,戈登。在我的示例中,第一个计数获取未完成的访问,而不是所有访问。在第二个中,我添加了 0,因为我担心将 null 添加到 mysql 中的计数中,我没有方便的实例来检查它的作用。我假设从您的回复中,它添加了一个 0?
  • 嘿,你们都做到了!这个答案也有效,而且更短更清晰!在选择最佳答案之前,我想先正确阅读每个答案,但我真的很喜欢这个答案。非常感谢您的努力!
  • @Metafaniel 需要注意的是,根据表的大小,左连接肯定比常规连接慢,尽管这个答案肯定更干净。
  • @SLin 感谢您的警告,我不是加盟专家,所以我会尽量记住您的建议。谢谢!
【解决方案2】:

如果两个查询都快速且运行良好,并且您只需要一个表中的结果,则可以使用联合选择 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

【讨论】:

  • 我试过了,但我得到了一个Error Code: 1221. Incorrect usage of UNION and ORDER BY 可能我对union 不太了解=P 我很少使用它。你能进一步解释一下吗?我只是将每个查询括在括号之间并添加了union =P
  • @Metafaniel 为您添加了一个示例。我假设您想区分 Visits 和 DoneVisists。
  • 谢谢它似乎很有用,我通过这个例子更好地理解了union,但是我的结果集中没有数字,而是BLOB 登录。任何想法为什么?再次感谢
  • @Metafaniel 你有一个 BLOB 符号作为结果集?这很奇怪,我没有你的桌子,所以我不能对此发表评论。尝试在查询中放置默认值 0 而不是查询中的“”。你用什么来运行 mysql 查询?
  • 啊,如果你想让 ad_st_id_state 加入这两个结果,那么 Ollie 的答案肯定会更好。 Union 仅用于合并结果集。
【解决方案3】:

您的两个查询都干净高效。因此,只需将它们连接在一起进行演示,将它们中的每一个都视为子查询。

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

【讨论】:

  • 我已经设法按照你的回答,我得到了想要的结果!是的,比我尝试做的要快得多。你是对的,我只需要一个三明治嘿嘿。我试图首先找出其他答案,但可能这个答案就是答案。谢谢
【解决方案4】:

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

为了创建该“条件”,您可以使用 LEFT OUTER JOIN 而不是 INNER JOIN

OUTER JOININNER 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 记录时,它会为零。

【讨论】:

  • 我理解你的意思,但在这种情况下,“左外连接”返回访问值,inner join 返回完成访问值,但我需要在一个结果集中两者。感谢您的回答
  • 这就是它的本意。我现在看到 id_visit 是计算 DISTINCT 的错误列。应该是vft_vi_id_visit。我相应地更新了我的答案。不过,所描述的概念正是您所需要的,正如您接受的答案所证明的那样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-20
  • 2019-08-08
  • 1970-01-01
  • 2012-02-15
  • 2010-11-18
  • 2016-03-17
相关资源
最近更新 更多