我在最小视图这里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
- 可能会考虑将结果存储在数据库,并从那里得到的。唯一的问题是旧订单可以修改,如果发生这种情况,总数将需要重建。
任何其他方式来重写查询以获得所需的结果?
请参阅最新的问题。 – James
参见已添加...... –
对不起,它在派生表上有。所以刚刚从粘贴的代码中剥离了它。我会尝试使用临时表方法来查看是否有所改进。 –
James