I have a stored procedure in mysql and it works but it's running so slow like 120 seconds. I need it to be faster than that. How can i optimize this?
CREATE DEFINER=`xxxx`@`%` PROCEDURE `sp_test`( IN merchantId int, IN startDate VARCHAR(50),IN endDate VARCHAR(50))
BEGIN
SELECT
inventory.sku AS 'SKU',
inventory.description AS 'Description',
inventory_category.name as 'Category',
inventory_sub_category.name as 'Sub Category',
IFNULL(tbl_ticket.price,0) AS 'Unit Price',
IFNULL(tbl_ticket.quantity_total,0) AS 'Total Issued',
IFNULL(ROUND(tbl_ticket.price,2),0) * IFNULL(tbl_ticket.quantity_total,0) AS 'Gross Amount',
IFNULL(tbl_ticket.promo_code_discount, 0 ) AS 'Total Discount',
ROUND((IFNULL(tbl_ticket.price,0) * IFNULL(tbl_ticket.quantity_total,0) ) - IFNULL(tbl_ticket.promo_code_discount, 0 ),2) AS 'Net Amount'
FROM inventory
LEFT JOIN inventory_category ON inventory.inventory_category_id = inventory_category.id
LEFT JOIN inventory_sub_category ON inventory.inventory_sub_category_id = inventory_sub_category.id
LEFT JOIN(
SELECT inventory.sku, inventory.price, channel.merchant_id, SUM(ticket.quantity_total) as 'quantity_total',
SUM(ticket.promo_code_discount) as 'promo_code_discount'
FROM inventory
JOIN v_transaction_items_details ON inventory.sku = v_transaction_items_details.item_name
JOIN ticket on ticket.id = v_transaction_items_details.ticket_id
JOIN transaction ON ticket.transaction_id = transaction.id
JOIN channel ON channel.sub_agent_id = ticket.reseller_id
WHERE CAST(transaction.time as DATE) BETWEEN startDate AND endDate
AND transaction.payment_status = 2
AND ticket.level = 1
AND ticket.type = 1
GROUP BY inventory.sku, inventory.price, channel.merchant_id) tbl_ticket on tbl_ticket.sku = inventory.sku
WHERE inventory.status = 1 AND (inventory.merchant_id = merchantId OR tbl_ticket.merchant_id = merchantId)
GROUP BY inventory.sku,inventory.description,inventory_category.name,inventory_sub_category.name;
END
I appreciate anyone that could help me on this. If i were to use caching how does that work?
CodePudding user response:
The appropriate way to troubleshoot this kind of performance trouble is to work directly with the query in the SP. Start with any subqueries, optimize them, and work your way out to the top level query. Only then work on the SP itself.
You have a subquery containing this WHERE clause.
WHERE CAST(transaction.time as DATE) BETWEEN startDate AND endDate
AND transaction.payment_status = 2
This has a problem: it cannot exploit any index on the time
column. Try refactoring it like so.
WHERE startDate <= transaction.time
AND transaction.time < endDate INTERVAL 1 DAY
AND transaction.payment_status = 2
Notice the <
, not <=
, at the end of the date/time range.
Then create a compound index on those two columns, putting the equality-search column first and the range-search column second. Like this.
ALTER TABLE transaction ADD INDEX status_time
(payment_status, time);
You didn't tell us much about your data, so this is a guess. But range searches of the form
function(column) BETWEEN this AND that
are usually a good place to start working on query performance. The use of a function to modify a column value in a WHERE clause often defeats the use of indexes. Read about sargability.
Also, the use of BETWEEN on date/time values is often a code smell: a hint that something is worth checking for mistakes or confusion.
CodePudding user response:
Do those Joins need to be LEFT
? A LEFT JOIN
with a subquery is a performance problem.
So is OR
. Switching to a UNION
may be the solution. (But the GROUP BY
adds complexity.)
What is the datatype of transaction.time
? If it is DATE
, then the CAST
is unnecessary. On the other hand, if startDate and endDate are
DATEs, then
DATETIMEmight work well. Let's see
SHOW CREATE TABLE`.
Meanwhile, these indexes may help:
inventory: INDEX(status, merchant_id, inventory_category_id)
inventory: INDEX(sku, price)
channel: INDEX(sub_agent_id, merchant_id)
ticket: INDEX(level, type, id)
v_transaction_items_details: INDEX(item_name, ticket_id)
transaction: INDEX(time, payment_status, id)