JOIN 语法中的 MySQL 相关子查询

Posted

技术标签:

【中文标题】JOIN 语法中的 MySQL 相关子查询【英文标题】:MySQL correlated subquery in JOIN syntax 【发布时间】:2010-10-06 12:49:11 【问题描述】:

我想通过指定 innertable.id = outertable.id 为内部查询提供 WHERE 条件。但是,mysql (5.0.45) 报告“'where 子句'中的未知列'outertable.id'”。这种查询可以吗?

内部查询使用 GROUP BY 将行转为列。这可以完全在外部查询中执行,但由于额外的连接可能会产生额外的开销。

或者,我可以在内部查询中省略 WHERE 条件,而是指定 ON outertable.id = innerquery.id,但它会获取整个内部查询行集以再次加入外部查询,这是低效的。

实际的 SQL 如下所示:

select t.ticketid, u.userid, t.fullname, u.loginapi_userid, t.email, tp.subject, tp.contents, a.PhoneNumber, a.Location, a.Extension, a.BusinessUnit, a.Department
from swtickets t
inner join swticketposts tp on t.ticketid = tp.ticketid
inner join swusers u on t.userid = u.userid
left join
  (
  select
  cfv.typeid,
  min(case cfv.customfieldid when 1 then cfv.fieldvalue end) as 'PhoneNumber',
  min(case cfv.customfieldid when 3 then cfv.fieldvalue end) as 'Location',
  min(case cfv.customfieldid when 5 then cfv.fieldvalue end) as 'Extension',
  min(case cfv.customfieldid when 8 then cfv.fieldvalue end) as 'BusinessUnit',
  min(case cfv.customfieldid when 9 then cfv.fieldvalue end) as 'Department'
  from swcustomfieldvalues cfv
  where cfv.typeid = t.ticketid
  group by cfv.typeid
  ) as a on 1 = 1
where t.ticketid = 2458;

【问题讨论】:

我最初的问题是,“这种类型的查询可能吗?” (关于 MySQL 5.0)。更改架构或对应用程序代码进行改进不在问题主题范围内。 【参考方案1】:

您的问题的答案是否定的,不可能像您那样引用相关名称。派生表是在外部查询开始评估连接之前由内部查询生成的。因此,内部查询无法使用 ttpu 等相关名称。

为了解决这个问题,我建议在内部查询中使用相同的常量整数值,然后在外部查询中使用真实条件而不是1=1 连接派生表。

SELECT t.ticketid, u.userid, t.fullname, u.loginapi_userid, t.email,
  tp.subject, tp.contents, a.PhoneNumber, a.Location, a.Extension,
  a.BusinessUnit, a.Department
FROM swtickets t
 INNER JOIN swticketposts tp ON (t.ticketid = tp.ticketid)
 INNER JOIN swusers u ON (t.userid = u.userid)
 LEFT OUTER JOIN (
  SELECT cfv.typeid,
    MIN(CASE cfv.customfieldid WHEN 1 THEN cfv.fieldvalue END) AS 'PhoneNumber',
    MIN(CASE cfv.customfieldid WHEN 3 THEN cfv.fieldvalue END) AS 'Location',
    MIN(CASE cfv.customfieldid WHEN 5 THEN cfv.fieldvalue END) AS 'Extension',
    MIN(CASE cfv.customfieldid WHEN 8 THEN cfv.fieldvalue END) AS 'BusinessUnit',
    MIN(CASE cfv.customfieldid WHEN 9 THEN cfv.fieldvalue END) AS 'Department'
  FROM swcustomfieldvalues cfv
  WHERE cfv.typeid = 2458
  GROUP BY cfv.typeid
  ) AS a ON (a.typeid = t.ticketid)
WHERE t.ticketid = 2458;

【讨论】:

【参考方案2】:

您正在使用实体-属性-值设计,如果您尝试生成常规结果集,最终将无法使其具有可扩展性。不要尝试在一个查询中执行此操作。

相反,首先查询您的规范化表:

SELECT t.ticketid, u.userid, t.fullname, u.loginapi_userid, t.email, 
  tp.subject, tp.contents
FROM swtickets t
 INNER JOIN swticketposts tp ON (t.ticketid = tp.ticketid)
 INNER JOIN swusers u ON (t.userid = u.userid)
WHERE t.ticketid = 2458;

然后查询您的自定义字段,将结果放在结果集的多行上:

SELECT cfv.customfieldid, cfv.fieldvalue
FROM swcustomfieldvalues cfv
WHERE cfv.typeid = 2458;

您将在结果集中获得多行,每个自定义字段一行:

+---------------+--------------+
| customfieldid | fieldvalue   |
+---------------+--------------+
|             1 | 415-555-1234 |
|             3 | Third office |
|             5 | 123          |
|             8 | Support      |
|             9 | Engineering  |
+---------------+--------------+

然后您需要编写应用程序代码,以循环方式将结果集字段映射到应用程序对象字段。

以这种方式使用实体-属性-值表在性能和代码维护方面更具可扩展性。

【讨论】:

此建议的完成方式与原始问题查询类似(将“where cfv.typeid = t.ticketid”更改为“where cfv.typeid = 2458”);相反,它依赖于数据库外部的代码来执行数据透视。 是的,完全正确。在 EAV 的情况下,没有办法在一次查询中使其高效。您应该在应用程序代码中进行数据透视。【参考方案3】:

我会用多个连接来编写它。当您说它“可能会产生额外的开销”时,这告诉我您尚未对其进行确定。如果您有不错的索引,则连接应该很简单。

这也只是显示了通用“hold all”表格设计模式的缺陷之一。

【讨论】:

确实如此。该设计称为实体-属性-值。它以多种方式破坏了规范化,而且非常难以使用。 在内部查询 (cfv) 中使用“WHERE cfv.typeid = 2458”会导致由于缺少索引而对 cfv 进行讨厌的表扫描,但是,忽略此条件会使事情变得更糟,从而导致除了对派生查询进行额外的表扫描之外,还有“使用临时;使用文件排序”。 我不确定您所说的“内部查询”是什么意思。将有 5 个新的内部连接,所以也许这就是您的意思。我希望您的 CFV 表在 typeid 上有一个索引。我可能会按顺序在 typeid 和 customfieldid 上有一个聚集索引。【参考方案4】:

我的建议将是你以效率为由排除的。例如。省略 where 子句并使用连接(根据 t.ticketid = a.ticketid)

您是否能够通过一些具体的例子来证明您对低效率的看法?我知道您在说什么,但是无论您使用外部查询中的每一行的任何方法都将连接到内部查询中的每一行,因此根据执行计划,它可能不会像您怀疑的那样低效?

【讨论】:

【参考方案5】:

我想问题是'cfv.typeid = t.ticketid'?我的想法是,虽然 MySQL 支持相关子查询,但您尝试做的事情似乎在连接中可能会失败,因为“内部”查询并不像它那样真正“在”查询的其余部分“内部”在 WHERE 子句中。但看起来您可以将 where 子句从子查询中取出,并在 a.typeid = t.ticketid 上设置连接条件。

【讨论】:

抱歉,这是查询的另一个变体遗留下来的。它与产生的实际错误无关,因此我已将其从上述问题中删除。

以上是关于JOIN 语法中的 MySQL 相关子查询的主要内容,如果未能解决你的问题,请参考以下文章

Mysql join with subquery join 使用相关名称

SELECT 中的子查询或 JOIN 中的子查询?

在 MS Query 中使用多个 INNER JOIN 的 FROM 子句中的子查询的语法

mysql连接查询,子查询,联合查询

Mysql JOIN 子查询

MySQL高级第八篇:关联查询子查询和排序相关优化