这个查询看起来优化了吗?

Posted

技术标签:

【中文标题】这个查询看起来优化了吗?【英文标题】:Does this query look optimized? 【发布时间】:2008-10-02 09:39:33 【问题描述】:

我正在为一个应用程序编写查询,该应用程序需要列出所有产品及其购买次数。

我想出了这个并且它有效,但我不太确定它的优化程度。由于我大量使用 ORM,我的 SQL 真的很生疏,但在这种情况下,查询是一个更优雅的解决方案。

您能在查询中发现任何错误(方法明智)吗?


SELECT  products.id, 
        products.long_name AS name, 
        count(oi.order_id) AS sold
FROM    products
LEFT OUTER JOIN 
      ( SELECT * FROM orderitems
        INNER JOIN orders ON orderitems.order_id = orders.id 
        AND orders.paid = 1 ) AS oi 
      ON oi.product_id = products.id
GROUP BY products.id

架构(带有相关字段)如下所示:

*orders*      id, paid
*orderitems*  order_id, product_id
*products*    id

更新

这是给 mysql

【问题讨论】:

【参考方案1】:

我不确定 "(SELECT *" ... 业务。

这执行(总是一个好的开始),我认为与发布的内容相同。

SELECT  products.id, 
    products.long_name AS name, 
    count(oi.order_id) AS sold
FROM    products
LEFT OUTER JOIN
    orderitems AS oi
        INNER JOIN 
            orders 
            ON oi.order_id = orders.id AND orders.paid = 1
    ON oi.product_id = products.id
GROUP BY products.id

【讨论】:

内连接周围没有括号,您确定这给出了与原始查询相同的答案吗? lassevek,它确实给出了正确的答案,我猜 MySQL 正确地解释了它,因为它是一个嵌套连接。不过我还是加了括号。 那么,MySQL 不反对 SELECT products.long_name 既不是 GROUP BY 列也不是聚合?令人印象深刻。 @ΤZΩΤZΙΟΥ:products.long_name 可能是 products.id 的函数依赖,所以它应该给出正确的结果。【参考方案2】:

这是为我们这些筑巢障碍的人提供的解决方案。 (当我开始嵌套连接时我很困惑)

SELECT  products.id, 
    products.long_name AS name, 
    count(oi.order_id) AS sold
FROM orders 
    INNER JOIN orderitems  AS oi ON oi.order_id = orders.id AND orders.paid = 1
    RIGHT JOIN products ON oi.product_id = products.id
GROUP BY products.id

但是,我在 MS SQL Server 上测试了您的解决方案,迈克和我的,查询计划是相同的。我不能代表 MySql,但如果 MS SQL Server 可以作为参考,您可能会发现所有三种解决方案的性能相当。如果是这种情况,我猜你会选择最清楚的解决方案。

【讨论】:

谢谢!我检查了 MySQL 中的查询计划,看起来你和 Mike 的查询计划是一样的,我的解决方案又迈出了一步。【参考方案3】:

它会给你正确的答案吗?

除了只是修改它以摆脱内部查询中的 SELECT 之外,我看不出它有什么问题。

【讨论】:

【参考方案4】:

您有“左外连接”,这可能是一个性能问题,具体取决于您的数据库。 上次我记得它在 MySQL 上造成了地狱,它在 SQLite 中不存在。我认为Oracle可以处理它,我猜DB和MSSQL也是。

编辑:如果我没记错的话 LEFT OUTER JOIN 在 MySQL 上可能会慢几个数量级,但如果我在这里过时了,请纠正我 :)

【讨论】:

【参考方案5】:

未经测试的代码,但请尝试一下:

SELECT  products.id,
    MIN(products.long_name) AS name, 
    count(oi.order_id) AS sold
FROM    (products
LEFT OUTER JOIN orderitemss AS oi ON oi.product_id = products.id)
INNER JOIN orders AS o ON oi.order_id = o.id 
WHERE orders.paid = 1
GROUP BY products.id

我不知道 LEFT OUTER JOIN 是否需要括号,如果 MySQL 允许多个连接也不需要,但是 MIN(products.long_name) 只给出描述,因为对于每个 products.id 你只有一个描述.

也许括号需要在 INNER JOIN 周围。

【讨论】:

谢谢!您的查询的问题在于,它似乎否定了 LEFT OUTER JOIN,因为结果不会返回没有销售的产品。 那么括号应该在INNER JOIN周围。【参考方案6】:

这是一个子查询表单。

SELECT
  p.id,
  p.long_name AS name,
  (SELECT COUNT(*) FROM OrderItems oi WHERE oi.order_id in
    (SELECT o.id FROM Orders o WHERE o.Paid = 1 AND o.Product_id = p.id)
  ) as sold
FROM Products p

它的性能应该大致相当于连接表单。如果没有,请告诉我。

【讨论】:

以上是关于这个查询看起来优化了吗?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 'OR' 查询优化

优化广泛使用 IN 子句的 Oracle SQL 查询

关于SQL优化的21个小方法,你get了吗?

这35个Java代码优化细节,你用了吗

python代码耗时优化,你知道了吗?

使用 or 优化查询