2017-01-13 142 views
-1

我在最小視圖這裏4個表:MySQL的子查詢優化

銷售:

id 
has_discount 
discount_is_percentage 
discount_amount 
**sale_date_time** 
**order_status** 

Sales_items:

id 
**sales_id** 
has_discount 
discount_is_percentage 
discount_amount 
**product_id** (This can sometimes be null) 
price_inc_vat_per_item 
quantity 
vat_rate 
is_removed 

Sales_payments:

id 
**sales_id** 
payment_amount 
payment_change 
payment_method 

產品:

id 
product_name 

我有一個查詢,它可以即時計算折扣並報告。這在記錄總數低於100-200k的情況下效果很好。但隨着人數的增加,所花的時間非常緩慢。我想這是因爲我使用的子查詢。任何人都可以爲此發光。每個表上有一個client_id和outlet_id,可以將它們與系統中的其他用戶區分開來。

目前這些表格有1到3百萬行,有問題的客戶端有300k-600k。查詢需要30多秒。對於行數較少的其他人,甚至可以在幾秒鐘內得到它。有星星的是指數。如何改進查詢以獲得相同的預期結果?查詢我現在有:

SELECT DATE_FORMAT(CONVERT_TZ(sales.sale_date_time,'UTC','Europe/London'), 
       '%l%p') as title, count(*) as total_sales, SUM(sales_items.quantity 
        ) as total_quantities, 
     SUM(sales_items.price_before_line_discount) as price_before_line_discount, 
     SUM(sales_items.price_before_line_discount-sales_items.line_discount) as price_after_line_discount, 
     SUM(sales_items.vat_rated_sales) as vat_rated_sales_before_discount, 
     SUM(sales_items.zero_rated_sales) as zero_rated_sales_before_discount, 
     SUM(sales_items.total_vat_only) as total_vat_only_before_discount, 
     SUM(sales_payments.payment_taken) as payment_taken, SUM(sales_items.line_discount) as total_line_discount, 
     SUM(sales_payments.payment_cash) as payment_cash, SUM(CASE WHEN sales.has_discount=1 
       AND sales.discount_is_percentage=0 THEN sales.discount_amount WHEN sales.has_discount=1 
       AND sales.discount_is_percentage=1 THEN ((sales_items.price_before_line_discount-sales_items.line_discount)*sales.discount_amount/100) WHEN sales.has_discount=0 THEN 0 END 
      )as total_sales_discount, 
     SUM(CASE WHEN sales.has_discount=1 THEN CASE WHEN discount_is_percentage=0 THEN (sales_items.vat_rated_sales*sales.discount_amount)/(sales_items.price_before_line_discount-sales_items.line_discount) WHEN discount_is_percentage=1 THEN (sales_items.vat_rated_sales*((sales_items.price_before_line_discount-sales_items.line_discount)*sales.discount_amount/100))/(sales_items.price_before_line_discount-sales_items.line_discount) END ELSE 0 END)as vat_rated_sales_discount, 
     SUM(CASE WHEN sales.has_discount=1 THEN CASE WHEN discount_is_percentage=0 THEN (sales_items.zero_rated_sales*sales.discount_amount)/(sales_items.price_before_line_discount-sales_items.line_discount) WHEN discount_is_percentage=1 THEN ((sales_items.zero_rated_sales*((sales_items.price_before_line_discount-sales_items.line_discount)*sales.discount_amount/100))/(sales_items.price_before_line_discount-sales_items.line_discount)) END ELSE 0 END)as zero_rated_sales_discount, 
     SUM(CASE WHEN sales.has_discount=1 THEN CASE WHEN discount_is_percentage=0 THEN (sales_items.total_vat_only*sales.discount_amount)/(sales_items.price_before_line_discount-sales_items.line_discount) WHEN discount_is_percentage=1 THEN (sales_items.total_vat_only*((sales_items.price_before_line_discount-sales_items.line_discount)*sales.discount_amount/100))/(sales_items.price_before_line_discount-sales_items.line_discount) END ELSE 0 END)as total_vat_only_discount 
    FROM `sales` 
    left join 
    (
     SELECT sales_id, SUM(quantity) as quantity, SUM(price_inc_vat_per_item*quantity) AS price_before_line_discount, 
       SUM(CASE WHEN has_discount=1 
         AND discount_is_percentage=0 THEN discount_amount WHEN has_discount=1 
         AND discount_is_percentage=1 THEN ((price_inc_vat_per_item*quantity)*discount_amount/100) WHEN has_discount=0 THEN 0 END 
        )as line_discount, 
       SUM(CASE WHEN vat_rate>0 THEN CASE WHEN has_discount=1 
         AND discount_is_percentage=0 THEN ((price_inc_vat_per_item*quantity)-discount_amount) WHEN has_discount=1 
         AND discount_is_percentage=1 THEN ((price_inc_vat_per_item*quantity)-((price_inc_vat_per_item*quantity)*discount_amount/100)) WHEN has_discount=0 THEN (price_inc_vat_per_item*quantity) END ELSE 0 END 
        )as vat_rated_sales, 
       SUM(CASE WHEN vat_rate=0 THEN CASE WHEN has_discount=1 
         AND discount_is_percentage=0 THEN ((price_inc_vat_per_item*quantity)-discount_amount) WHEN has_discount=1 
         AND discount_is_percentage=1 THEN ((price_inc_vat_per_item*quantity)-((price_inc_vat_per_item*quantity)*discount_amount/100)) WHEN has_discount=0 THEN (price_inc_vat_per_item*quantity) END ELSE 0 END 
        )as zero_rated_sales, 
       SUM(CASE WHEN vat_rate>0 THEN CASE WHEN has_discount=1 
         AND discount_is_percentage=0 THEN ((price_inc_vat_per_item*quantity)-discount_amount)-((price_inc_vat_per_item*quantity)-discount_amount)/(1+(vat_rate/100)) WHEN has_discount=1 
         AND discount_is_percentage=1 THEN ((price_inc_vat_per_item*quantity)-((price_inc_vat_per_item*quantity)*discount_amount/100))-((price_inc_vat_per_item*quantity)-((price_inc_vat_per_item*quantity)*discount_amount/100))/(1+(vat_rate/100)) WHEN has_discount=0 THEN (price_inc_vat_per_item*quantity)-(price_inc_vat_per_item*quantity)/(1+(vat_rate/100)) END ELSE 0 END 
        )as total_vat_only 
      FROM sales_items 
      WHERE client_id='0fe26d93-775f-440c-a119-13cbcb6cbc0c' 
       AND is_removed=0 
      GROUP BY sales_id 
    ) as sales_items ON `sales`.`id` = `sales_items`.`sales_id` 
    left join 
    (
     SELECT sales_id, SUM(payment_amount-payment_change) payment_taken, 
       SUM(CASE WHEN payment_method='CASH' THEN (payment_amount-payment_change) ELSE 0 END) as payment_cash 
      FROM sales_payments 
      WHERE client_id='0fe26d93-775f-440c-a119-1396c36cbc0c' 
      GROUP BY sales_id 
    ) as sales_payments ON `sales`.`id` = `sales_payments`.`sales_id` 
    WHERE `sales`.`client_id` = '0fe26d93-775f-440c-a119-1396c36cbc0c' 
     and `sales`.`outlet_id` = 'd5b74bdf-5cef-4455-bf99-13cbcb6cbc0c' 
     and `sales`.`order_status` = 'COMPLETED' 
     and `sale_date_time` >= '2016-01-28 00:00:00' 
     and `sale_date_time` <= '2016-11-28 23:59:00' 
    GROUP BY HOUR(CONVERT_TZ(sales.sale_date_time,'UTC','Europe/London')) 
    ORDER BY `sale_date_time` ASC 

UPDATE:

要回答@裏克 - 詹姆斯

  • 我需要sale_date_time這是一個時間字段進行排序的問題有關。小組需按小時報告。它也有天,月 - 年等取決於所查詢的時期。因爲設計原因不得不使用UUID。整個數據庫大約8GB,其中大部分都是這四張表。由於我有很多外鍵約束,索引長度比實際數據大小要大。

這是在亞馬遜極光與15GB內存。

銷售表: 0.5GB數據1.3GB指數

銷售項目: 1.3GB數據3.2GB指數

銷售款項: 0.5GB數據1.1GB指數

所有表覈對是utf8_unicode_ci。

  • 它使用的是Aurora 5.6,它是MySQL 5.6。這裏是解釋選擇。

ID SELECT_TYPE表鍵入possible_keys鍵key_len REF行過濾額外

1主銷售REF sales_client_id_outlet_id_foreign,sales_client_id_index,sales_outlet_id_index,sales_sale_date_time_index,sales_order_status_index sales_client_id_index 108常量5352使用 指數條件;在哪裏使用;使用臨時;使用文件排序

1 PRIMARY REF 108 MyDB.sales.id 10

1 PRIMARY REF 108 MyDB.sales.id 10

3 DERIVED sales_payments REF sales_payments_client_id_outlet_id_foreign,sales_payments_client_id_index sales_payments_client_id_outlet_id_foreign 108常量5092使用索引條件;在哪裏使用;使用臨時;使用文件排序

2 DERIVED sales_items REF sales_items_client_id_outlet_id_foreign,sales_items_client_id_index sales_items_client_id_outlet_id_foreign 108常量13340使用 指數條件;在哪裏使用;使用臨時;使用文件排序

2衍生產品eq_ref PRIMARY,products_id_unique PRIMARY 108 MyDB.sales_items.product_id 1

  • 可能會考慮將結果存儲在數據庫,並從那裏得到的。唯一的問題是舊訂單可以修改,如果發生這種情況,總數將需要重建。

任何其他方式來重寫查詢以獲得所需的結果?

回答

0
  • ORDER BYGROUP BY不必要地不同時,需要額外的排序傳遞。
  • 當數據大於可以緩存在RAM中時,UUID效率非常低。桌子有多大? `innodb_buffer_pool_size的價值是什麼?你有多少RAM?
  • LEFT JOIN (SELECT ...)直到5.6時效率非常低。請提供EXPLAIN SELECT ...以查看它是否已優化。你使用什麼版本?
  • 更糟糕的是LEFT JOIN (SELECT ...) LEFT JOIN (SELECT ...)加入:由於我沒有看到「自動密鑰」,這是不好的。它讓我懷疑它是否真的是MySQL 5.6。
  • 構建和維護「彙總表」可能是最終的答案。它可能會有PRIMARY KEY,包括client_id,outlet_id,order_status和sale_HOUR。
  • 是否子查詢本身運行緩慢?如果是這樣,請單獨提問以專注於子查詢。請提供SHOW CREATE TABLE的輸出;你的表格描述中缺少很多細節 - 索引,數據類型,大小,排序規則等。加:仍然需要這個;還有一些事情要檢查。一個可能的解決方案:CREATE TEMPORARY TABLE與兩個LEFT JOIN SELECTs中的每一個;然後使用它們。
+0

請參閱最新的問題。 – James

+0

參見已添加...... –

+0

對不起,它在派生表上有。所以剛剛從粘貼的代碼中剝離了它。我會嘗試使用臨時表方法來查看是否有所改進。 – James