优化从销售表购买的每个客户的选定产品详细信息

时间:2019-01-24 23:22:01

标签: mysql sql performance select

我有4个表,客户,产品,销售和销售商品。我使用下面的查询从中提取数据。

SELECT (
            SELECT c.name 
            FROM Customers c
            WHERE s.customer_id=c.id
            ) customer
    ,(
        Select group_concat(description)
        FROM (
            SELECT si.id
                ,si.sale_id
                ,concat("x", si.Qty, " ", p.name, " ",(si.total)) description
            FROM Sale_Items si
            LEFT JOIN Products p ON p.id = si.product_id
            ) p
        where s.id = Sale_ID
        GROUP BY Sale_ID
        ) detail,
                            s.total
FROM Sales s

查询会产生结果,但结果变慢,仅记录2000条(需要114秒才能完成)

Customer    Product            Total
--------------------------------------
James       x1 ItemA 10.00  75.00
            x3 ItemB 15.00
            x1 ItemC 20.00

Mark        x2 ItemA 10.00  50.00
            x2 ItemB 15.00

Bisi        x1 ItemC 20.00  30.00
            x2 ItemA 10.00

我怎样才能更快?

在这里尝试过

https://www.db-fiddle.com/f/pkL2HtsT659EXgRSevFSAm/4

1 个答案:

答案 0 :(得分:1)

如果我们要坚持使用相关子查询,则可以消除内联视图p

将从Sales中检索到的每一行都变为现实。外部查询的WHERE子句中的谓词不会“推”入视图。因此,物化视图(或MySQL术语中的“派生表”)将是一个完整的集合,然后,我们将从中挑选出几行。我们将对Sales中的每一行重复一次。

展开该派生表应该为我们带来一些性能优势。对于从Sales返回的少量行并定义了合适的索引,这将是一种合理的方法。也就是说,如果我们使用WHERE子句限制外部查询检查的行数。由于有大量的行,那些相关的子查询将降低性能。

SELECT ( SELECT c.name 
           FROM Customers c
          WHERE c.id = s.customer_id
       ) AS customer

     , ( SELECT GROUP_CONCAT(CONCAT('x',si.Qty,' ',p.name,' ',si.total) ORDER BY p.name SEPARATOR '\r\n')
           FROM Sale_Items si
           LEFT
           JOIN Products p
             ON p.id = si.product_id
          WHERE si.sale_id = s.id
       ) AS detail

     , s.total

  FROM Sales s

 WHERE ... 

 ORDER
    BY ...

如果查询返回的是Sales的所有行,而我们正在进行整个Bloomin'设置,那么我倾向于避免相关的子查询。 (这是因为子查询是针对外部查询返回的每一行执行的。就性能而言,这些子查询将吃掉我们的午餐,并且返回了大量行。)

假设idcustomers中是唯一的,那么使用连接操作通常会更好。

SELECT c.name AS customer
     , d.detail
     , s.total
  FROM Sales s
  LEFT
  JOIN Customers c
    ON c.id = s.customer_id
  LEFT
  JOIN ( SELECT si.sale_id
              , GROUP_CONCAT(CONCAT('x',si.Qty,' ',p.name,' ',si.total) ORDER BY p.name SEPARATOR '\r\n') AS detail
           FROM Sale_Items si
           LEFT
           JOIN Products p
             ON p.id = si.product_id
          GROUP
             BY si.sale_id
       ) d
    ON d.sale_id = s.id
 ORDER
    BY ...

内联视图d对于大型集合将变得昂贵;但至少我们只进行一次查询,将结果具体化为“派生表”。然后,外部查询可以运行,并从派生表中检索行。