0

这是我的查询:

SELECT `products`.*, SUM(orders.total_count) AS revenue,
    SUM(orders.quantity) AS qty, ROUND(AVG(product_reviews.stars)) as avg_stars 
FROM `products` 
LEFT JOIN `orders`
    ON (`products`.`id` = `orders`.`product_id`) AND
    (`orders`.`status` = 'delivered' OR `orders`.`status` = 'new') 
LEFT JOIN product_reviews
    ON (products.id = product_reviews.product_id)
GROUP BY `products`.`ID`
ORDER BY products.ID DESC
LIMIT 10
OFFSET 0

当我有第二个左连接时,我的第一个左连接数据、收入和订单表中的数量给我的值根本不正确(太高了,很多双打?)

这个问题

我得到了获得半笛卡尔产品的方向,因此对产品的两次评论使数量翻了一番,我相信这是我的问题。

如何解决?

4

3 回答 3

3

问题是product_reviewsand orders 表的每个产品 ID 可以有不止一行。解决此问题的一种方法是使用子查询:

SELECT `products`.*, 
  o.revenue,
  o.qty, 
  ROUND(avg_stars) as avg_stars 
FROM `products` 
LEFT JOIN
(
  select `product_id`, 
    sum(total_count) revenue,
    sum(quantity) qty
  from `orders`
  where `status` in ('delivered', 'new')
  group by `product_id`
) o
  ON `products`.`id` = o.`product_id`
LEFT JOIN
(
  select product_id, avg(stars) avg_stars
  from product_reviews
  group by product_id
) pr
    ON (products.id = pr.product_id)
ORDER BY products.ID DESC
LIMIT 10
OFFSET 0
于 2013-01-22T23:29:18.343 回答
1

避免该问题的一种方法是在 SELECT 列表中使用相关子查询,而不是左连接。

SELECT p.*
     , SUM(o.total_count) AS revenue
     , SUM(o.quantity) AS qty
     , ( SELECT ROUND(AVG(r.stars))
           FROM `product_reviews` r
          WHERE r.product_id = p.id 
       ) AS avg_stars
  FROM `products` p
  LEFT
  JOIN `orders` o
    ON o.product_id = p.id
   AND o.status IN ('delivered','new')
 GROUP BY p.id
 ORDER BY p.id DESC
 LIMIT 10
 OFFSET 0

这不是唯一的方法,也不一定是最好的方法,尤其是对于大型集合但考虑到子查询将运行最多 10 次(给定 LIMIT 子句),性能应该是合理的(给定适当的product_reviews(product_id,stars).

如果您要返回所有产品 ID,或者其中很大一部分,那么使用内联视图可能会提供更好的性能(避免在选择列表中执行相关子查询的嵌套循环)

SELECT p.*
     , SUM(o.total_count) AS revenue
     , SUM(o.quantity) AS qty
     , s.avg_stars
  FROM `products` p
  LEFT
  JOIN `orders` o
    ON o.product_id = p.id
   AND o.status IN ('delivered','new')
  LEFT
  JOIN ( SELECT ROUND(AVG(r.stars)) AS avg_stars
              , r.product_id
           FROM `product_reviews` r
          GROUP BY r.product_id 
       ) s
    ON s.product_id = p.id
 GROUP BY p.id
 ORDER BY p.id DESC
 LIMIT 10
 OFFSET 0

需要明确的是:原始查询的问题在于,产品的每个订单都与该产品的每个评论相匹配。

如果我对“半笛卡尔”一词的使用具有误导性或混淆性,我深表歉意。

我的意思是,您有两个不同的集合(产品的订单集和产品的评论集),并且您的查询正在生成这两个不同的“交叉产品”集,基本上将每个订单与每个评论“匹配”(针对特定产品)。

例如,给定 product_id 101 的三行,reviewsproduct_id 101 的两行orders,例如:

REVIEWS
pid  stars text
---  ----- --------------
101  4.5   woo hoo perfect
101  3     ehh
101  1     totally sucked


ORDERS
pid  date   qty 
---  -----  ---
101  1/13   100
101  1/22   7

您的原始查询基本上形成了一个包含六行的结果集,订单中的每一行都与评论中的所有三行相匹配:

id   date   qty   stars text
---  ----   ----  ----  ------------
101  1/13   100   4.5   woo hoo perfect
101  1/13   100   3     ehh
101  1/13   100   1     totally sucked
101  1/22   7     4.5   woo hoo perfect
101  1/22   7     3     ehh
101  1/22   7     1     totally sucked

然后,当应用 qty 上的 SUM 聚合时,返回的值比您预期的要大得多。

于 2013-01-22T23:34:00.277 回答
1

如果不看表模式就不容易解决这个问题,我建议你先看看你的聚合和分组依据语句,然后看看你的列默认值,你是如何处理空值的,还要看看聚合函数中的 DISTINCT。

如果所有其他方法都失败并且“优化”解决方案并不重要并且您的数据量很低,则仅在您需要值的表上执行 Sub Select,在 Sub Select on 1 table 中,您的行范围要窄得多,并且它将产生正确的结果。

我建议您在此处提供表模式。

于 2013-01-22T23:51:25.063 回答