[英]I have a fairly large SQL query that is being slowed by the GROUP BY and ORDER BY statements. What is the best way to optimize?
優化此查詢的最佳方法是什么,因為它最多需要 12 秒才能執行?
為查詢的大小道歉,它幾乎立即執行,沒有 group by 和 order by 語句。
我對 SQL 優化還很陌生,我只編碼了大約一年。 我沒有寫這個查詢,但我需要修復它。
SELECT
`products`.*,
`product_alternative`.`alternative_product_code`,
`product_alternative`.`id` as `product_alternative_id`,
`vat_rate`.`rate` AS `vat_rate`,
`product_images`.`filename` AS `product_image_file`,
`product_docs`.`filename` AS `product_doc_file`,
`suppliers`.`supplier_code`,
`suppliers`.`name` AS `supplier_name`,
`commission`.`commission` AS `supplier_commission`,
`categories`.`name` AS `category_name`,
`sub_categories`.`name` AS `subcategory_name`,
IF(`products`.`product_doc_id` = 0,
NULL,
CONCAT(
"/product-docs/",
`products`.`id`
)) AS `product_doc_url`,IF(`products`.`product_image_id` = 0, NULL, CONCAT(
"/product-images/",
`products`.`id`,
"/original/"
)) AS `original_image_url`,IF(`products`.`product_image_id` = 0, NULL, CONCAT(
"/product-images/",
`products`.`id`,
"/medium/"
)) AS `medium_image_url`,IF(`products`.`product_image_id` = 0, NULL, CONCAT(
"/product-images/",
`products`.`id`,
"/thumb/"
)) AS `thumb_image_url`,CASE
WHEN
`group_favourite`.`id` IS NOT NULL
THEN 1 ELSE 0
END
AS `group_favourite`
,CASE
WHEN
`product_favourite`.`org_id` != 0
AND `product_favourite`.`org_id` IS NOT NULL
THEN 1 ELSE 0
END
AS `favourite`
,CASE
WHEN
`product_favourite`.`org_id` != 0
AND `product_favourite`.`org_id` IS NOT NULL
THEN 1 ELSE 0
END
AS `favourite`,
`product_favourite`.`needs_authorisation`,
IF(`customer_personal_favourite`.`id` IS NOT NULL,1,0) AS `is_personal_favourite`
FROM
`products`
LEFT JOIN `vat_rate` ON `products`.`vat_rate_id` = `vat_rate`.`id`
LEFT JOIN `product_images` ON `products`.`product_image_id` = `product_images`.`id`
LEFT JOIN `product_docs` ON `products`.`product_doc_id` = `product_docs`.`id`
LEFT JOIN `orgs` AS `suppliers` ON `products`.`supplier_id` = `suppliers`.`id`
LEFT JOIN `commission` ON
`suppliers`.`id` = `commission`.`org_id`
AND `commission`.`status` = 1
AND DATE(`commission`.`start_date`) <= DATE('2020-07-15')
AND (
DATE(`commission`.`end_date`) >= DATE('2020-07-15')
OR `commission`.`end_date` = '0000-00-00'
)
LEFT JOIN `categories` AS `categories` ON
`products`.`category_id` = `categories`.`id`
LEFT JOIN `categories` AS `sub_categories` ON
`products`.`sub_category_id` = `sub_categories`.`id`
LEFT JOIN `product_attribute_product` ON
`product_attribute_product`.`product_id` = `products`.`id`
LEFT JOIN `product_alternative` ON
`product_alternative`.`product_original_id` = `products`.`original_id`
JOIN `orgs` AS `customer` ON
`customer`.`id` IN (320)
LEFT JOIN `product_supplier_account` ON
`product_supplier_account`.`product_id` = `products`.`id`
JOIN `supplier_account` AS `default_supplier_account` ON
`default_supplier_account`.`supplier_id` = `products`.`supplier_id`
AND `default_supplier_account`.`is_default` = 1
AND `default_supplier_account`.`is_deleted` = 0
JOIN `customer_to_supplier` ON
`customer_to_supplier`.`supplier_id` = `products`.`supplier_id`
AND `customer_to_supplier`.`customer_id` IN (`customer`.`id`)
AND `customer_to_supplier`.`allow_access` = 1
AND `customer_to_supplier`.`status` = 1
JOIN `customer_to_supplier_account` ON
`customer_to_supplier_account`.`customer_to_supplier_id` = `customer_to_supplier`.`id`
AND (
`customer_to_supplier_account`.`supplier_account_id` = `product_supplier_account`.`supplier_account_id`
OR `customer_to_supplier_account`.`supplier_account_id` = `default_supplier_account`.`id`
)
AND `customer_to_supplier_account`.`allow_access` = 1
AND `customer_to_supplier_account`.`is_deleted` = 0
LEFT JOIN `statement_products_categories` ON
`statement_products_categories`.`product_id` = `products`.`original_id`
LEFT JOIN `product_favourite` ON
`product_favourite`.`product_id` = `products`.`original_id`
AND `product_favourite`.`org_id` IN (`customer`.`id`)
LEFT JOIN `group_favourite` AS `group_favourite` ON
`products`.`original_id` = `group_favourite`.`product_id`
AND `group_favourite`.`group_id` IN (37)
LEFT JOIN `budget` AS `category_budget` ON
`category_budget`.`org_id` IN (`customer`.`id`)
AND `category_budget`.`budget_type_id` = 1
AND `category_budget`.`category_id` = `products`.`category_id`
AND
`category_budget`.`start_date` <= CURDATE()
AND (
`category_budget`.`final_date` >= CURDATE()
OR `category_budget`.`final_date` IS NULL
)
LEFT JOIN `budget` AS `supplier_budget` ON
`supplier_budget`.`org_id` IN (`customer`.`id`)
AND `supplier_budget`.`budget_type_id` = 3
AND `supplier_budget`.`supplier_id` = `products`.`supplier_id`
AND
`supplier_budget`.`start_date` <= CURDATE()
AND (
`supplier_budget`.`final_date` >= CURDATE()
OR `supplier_budget`.`final_date` IS NULL
)
LEFT JOIN `budget` AS `product_budget` ON
`product_budget`.`org_id` IN (`customer`.`id`)
AND `product_budget`.`budget_type_id` = 2
AND
`product_budget`.`start_date` <= CURDATE()
AND (
`product_budget`.`final_date` >= CURDATE()
OR `product_budget`.`final_date` IS NULL
)
LEFT JOIN `customer_personal_favourite` ON
`customer_personal_favourite`.`org_id` = `customer`.`id`
AND `customer_personal_favourite`.`product_original_id` = `products`.`original_id`
WHERE
0 OR (1 AND `products`.`status` = 1 AND ((
`products`.`parent_id` = 0
OR `products`.`parent_id` IS NULL
) OR (
`product_favourite`.`org_id` IS NOT NULL
AND `product_favourite`.`org_id` != 0
) ) AND `products`.`id` = `products`.`current_id` )
GROUP BY `products`.`id`
ORDER BY
`favourite` DESC,
`active_date` ASC,
`products`.`code` ASC,
`products`.`name` ASC,
`products`.`parent_id` ASC
LIMIT 0, 20
我曾經遇到過這個問題,當時我決定寫一篇關於這個的文章,如果有人想看看我是如何實現的,我已經從不同的來源編譯並優化了我的查詢。 請看看這個。 https://junaidtechblog.wordpress.com/2019/09/04/optimize-sql-query-groupby-having/
AND DATE(`commission`.`start_date`) <= DATE('2020-07-15')
AND ( DATE(`commission`.`end_date`) >= DATE('2020-07-15')
OR `commission`.`end_date` = '0000-00-00'
有幾個問題:
OR
很難優化。 找到一些方法來避免它——例如刪除這些行,或者使用一個遙遠的未來而不是過去的結束日期。SHOW CREATE TABLE
。DATE()
圍繞文字“2020-07-15”。 另一方面,這沒問題: product_budget
。 開始日期 <= start_date
()
ORDER BY `favourite` DESC,
`active_date` ASC,
除非您有 MySQL 8.0,否則沒有索引可以處理混合了 DESC 和 ASC 的ORDER BY
。 (由於其他原因,可能無法使用索引。)
WHERE 0 OR (1 AND ...
動態構建查詢,而不是使用像這樣的組合。
LEFT JOIN ... ON ... foo = 1
ON
應該 state 表是如何相關的。 foo = 1
感覺更像是應該在WHERE
子句中的東西(用於過濾)。 在ON
和WHERE
之間移動它可能會改變生成的行集。 但我質疑這是否應該是JOIN
(而不是LEFT JOIN
)或者測試應該在WHERE
中,或者它是否真的像你寫的那樣有效。
請提供EXPLAIN SELECT...
具有最大“行”的行可能是要關注的表。
GROUP BY `products`.`id`
很可能導致損壞的 output。 閱讀“only_full_group_by”。
這會導致潛在的加速。
做最少的工作來獲得一個滿足ORDER BY
和LIMIT
的id
列表。
然后JOIN
所有其他表,包括返回products
,以獲取其他列。 這可能會加快速度並擺脫上述錯誤。
在哪里實際將LEFT JOIN
更改為子查詢:
SELECT... `vat_rate`.`rate` AS `vat_rate`, ... LEFT JOIN `vat_rate` ON `products`.`vat_rate_id` = `vat_rate`.`id`...
-->
SELECT ...
( SELECT `rate` FROM vat_rate WHERE id = `products`.`vat_rate_id`
) AS `vat_rate`,
...
...
完成大部分工作,然后回來尋求更多建議。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.