【问题标题】:Rewriting SQL to JOINS instead of subqueries将 SQL 重写为 JOINS 而不是子查询
【发布时间】:2010-11-10 12:51:38
【问题描述】:

我在重写此查询以使用联接而不是两个子查询时遇到了一些麻烦。如果你明白我的意思,我很难解开它。

SELECT o.order_id, n.title, c.first_name, t1.name, o.product_id,
    (SELECT ttd2.tid FROM term_data ttd2, term_node ttn2 WHERE ttd2.vid = 5 AND ttn2.nid = p.nid AND ttd2.tid=ttn2.tid) AS tid,
    (SELECT ttd4.name FROM term_data ttd4, term_node ttn4 WHERE ttd4.vid = 8 AND ttn4.nid = p.nid AND ttd4.tid=ttn4.tid) AS month
    FROM orders o, products p, node n, customers c, term_data t1, term_node t2
    WHERE o.product_id = p.nid
    AND p.nid = n.nid
    AND o.customer_email = c.customer_email
    AND t2.tid = t1.tid
    AND t1.vid = 6
    AND n.nid = t2.nid

你能帮忙吗?或者提供一些线索/提示。

【问题讨论】:

  • 主子句表和子查询表之间没有关系 - 这是故意的吗?
  • @Roopesh Shenoy,其实是有的,不过你得用滚动条才能看到。
  • 是的,你是对的,我错过了“p”。对不起!

标签: sql mysql join subquery


【解决方案1】:

使用ANSI SQL-92语法重写它(即使用JOIN和ON子句),它应该更清晰。

现在你所有的JOINWHERE 子句都混合在一起,所以不容易看到关系。子查询不一定是问题;清理语法后,这应该会变得更加清晰。

【讨论】:

  • (迂腐点:)除了保留字'Month'外,问题中的代码已经是SQL-92语法了。
【解决方案2】:

重写为使用 ANSI-92 SQL 并略微简化以删除冗余连接,您的查询应如下所示:

SELECT o.order_id, 
       n.title, 
       c.first_name, 
       tdv6.name, 
       o.product_id,
       tdv5.tid,
       tdv8.name    month
FROM orders o
     join products p           on o.product_id = p.nid
     join node n               on p.nid = n.nid
     join customers c          on o.customer_email = c.customer_email
     join term_node tnv        on n.nid = tn.nid
     join term_data tdv6       on tn.tid = tdv6.tid AND tdv6.vid = 6
     left join term_data tdv5  on tn.tid = tdv5.tid AND tdv5.vid = 5
     left join term_data tdv8  on tn.tid = tdv8.tid AND tdv8.vid = 8

【讨论】:

  • 两个子查询都连接到 term_node - 但是,在主查询中已经有一个到 term_node 的内部连接。由于连接标准实际上是相同的(来自产品或节点的 nid - 它们在 nid 上相互内部连接,因此将始终具有相同的 nid 值),term_node 只需访问一次 - 与term_data 不同,它具有三种不同的连接条件。
  • 你确定不应该是SELECT DISTINCT... 吗?
  • (迂腐点:)使用保留字“月”违反 SQL-92。
  • @onedaywhen - 我很肯定它不应该是 SELECT DISTINCT - 除了最后两个 term_data 连接外,此查询在功能上与原始查询相同(不使用DISTINCT),而最后两个连接相当于子查询。原始中的每一个都应该只返回一个值(否则整个查询将失败) - 如果它们确实每个返回多个值,则应显示所有返回的值。
【解决方案3】:

通常:

将您的 SELECTed 列向上移动到主 SELECT 语句,将您的 FROM 子句移动到原始 JOIN 子句下方的 JOIN 子句,并将您的连接条件也移动到该行。您的 WHERE 子句可以保持原样。

正如@RedFilter 所说,使用JOINON 子句。我认为你在做笛卡尔,但由于语法我不确定。

例如(我不知道这是否对您的表结构有效,因为您没有提供):

SELECT o.order_id, n.title, c.first_name, t1.name, o.product_id,ttd2.tid as 'tid', ttd4.name as 'name'
FROM orders o
INNER JOIN products p ON o.product_id = p.nid
INNER JOIN node n ON AND p.nid = n.nid
INNER JOIN customers c ON o.customer_email = c.customer_email
INNER JOIN term_data t1 ON t2.tid = t1.tid
INNER JOIN term_node t2 ON n.nid = t2.nid 
INNER JOIN ...
WHERE n.nid = t2.nid
AND ttd2.vid = 5 
AND ttn2.nid = p.nid 
AND ttd2.tid=ttn2.tid)
AND t1.vid = 6
AND ...

【讨论】:

  • 我明白了!这很好地区分了关系和 where 子句。笛卡尔是什么意思?
  • 笛卡尔JOIN 是一个产品...基本上每个可能的表格组合都已给出。因此,如果您在表 A 中有 4 行,在表 B 中有 4 行,那么您的“产品”将是 16 行(A 中的每一行与 B 中的每一行配对,4x4)。使用上述语法的显式连接总是更好,因为如果你得到意想不到的结果,更容易找出关系并进行调整。
  • 我会明确使用INNER JOINLEFT OUTER JOIN等,以便您知道是否有必要的ONWHERE子句。否则,它只是暗示,因此更难判断何时有错误或遗漏。
  • 上述查询的当前问题是当子查询失败时,因为没有足够的信息,它们不会返回任何内容。当由于某种奇怪的原因对客户的引用不在数据库中时,它们也不会返回任何内容。
  • 我使用非常不一致的数据,这让我很烦恼。我想我会尝试重写我的查询以匹配您的建议。谢谢!
猜你喜欢
  • 2011-11-15
  • 1970-01-01
  • 2020-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-29
  • 2018-02-22
相关资源
最近更新 更多