mariadb(mysql) slow query fix - mysql

SELECT
CAST(`a`.`selldate` AS DATE) AS `regdate`,
`a`.`market` AS `market`,
`a`.`user_seq` AS `user_seq`,
COUNT(0) AS `complete`,
ROUND(SUM(`a`.`sell`) - SUM(`a`.`buy`) - SUM(`a`.`buy_fee`) - SUM(`a`.`sell_fee`),8) AS `profit`,
ROUND(SUM(`a`.`buy`),8) AS `price`,
ROUND(SUM(`a`.`sell_fee`),8) AS `sell_fee`,
ROUND(SUM(`a`.`buy_fee`),8) AS `buy_fee`,
(
SELECT SUM(`yangpago`.`funding`.`price`) AS `funding_fee`
FROM `yangpago`.`funding`
WHERE
DATE_FORMAT(FROM_UNIXTIME(`yangpago`.`funding`.`time`),'%Y-%m-%d') = CAST(`a`.`selldate` AS DATE)
AND
`yangpago`.`funding`.`user_seq` = `a`.`user_seq`
GROUP BY `regdate`,`yangpago`.`funding`.`user_seq`
) AS `funding_fee`
FROM (
SELECT
`v_order`.`cnt` AS `cnt`,
`v_order`.`market` AS `market`,
`v_order`.`user_seq` AS `user_seq`,
`v_order`.`selldate` AS `selldate`,
`v_order`.`sell_price` * `v_order`.`sell_amount` AS `sell`,
`v_order`.`buy_price` * `v_order`.`buy_amount` AS `buy`,
`v_order`.`sell_price` * `v_order`.`sell_amount` * `f`.`sell_fee` AS `sell_fee`,
`v_order`.`buy_price` * `v_order`.`buy_amount` * `f`.`buy_fee` AS `buy_fee`
FROM (`yangpago`.`v_order`
JOIN `yangpago`.`fee` `f`
ON (
`f`.`market` = `v_order`.`market`
AND
`f`.`user_seq` = `v_order`.`user_seq`
AND
CAST(`v_order`.`selldate` AS DATE) BETWEEN `f`.`startdate`
AND
IFNULL(`f`.`enddate`, CURRENT_TIMESTAMP())
)
)
WHERE `v_order`.`state` = 'COMPLETE') `a`
GROUP BY CAST(`a`.`selldate` AS DATE),`a`.`market`,`a`.`user_seq`
here is my balance view query.
this query is speed very slow. run time (1~2s)
i try funding subquery remove and check time 0.2s~0.3s
so, remove join query too. and check time 0.1s
all of sum column remove, time is 0.004s. i want 0.01s under. this query result.
how to fix it?

You are in effect reading the data twice once to create the virtual table a then again to aggregate - the virtual table is not necessary consider refactoring your code
SELECT
CAST(v_order.selldate AS DATE) AS regdate,
v_order.market AS market,
v_order.user_seq AS user_seq,
COUNT(0) AS complete,
sum(v_order.sell_price * v_order.sell_amount) -
SUM(v_order.buy_price * v_order.buy_amount) -
SUM(v_order.buy_price * v_order.buy_amount * f.buy_fee) -
SUM(v_order.sell_price * v_order.sell_amount * f.sell_fee) as profit,
SUM(v_order.buy_price * v_order.buy_amount) AS price,
SUM(v_order.sell_price * v_order.sell_amount * f.sell_fee) AS sell_fee,
SUM(v_order.buy_price * v_order.buy_amount * f.buy_fee) AS buy_fee,
(SELECT SUM(funding.price) AS funding_fee
FROM funding
WHERE DATE_FORMAT(FROM_UNIXTIME(funding.time),'%Y-%m-%d') = CAST(a.selldate AS DATE)
AND funding.user_seq = v_order.user_seq
) AS funding_fee
FROM v_order
JOIN fee f ON f.market = v_order.market
AND f.user_seq = v_order.user_seq
AND CAST(v_order.selldate AS DATE) BETWEEN f.startdate
AND coalesce(f.enddate, CURRENT_TIMESTAMP())
WHERE v_order.state = 'COMPLETE'
GROUP BY regdate,market,user_seq;

Related

MySQL variable as result of subquery with elements of other table

I have a working query:
SELECT count(*),
AVG(
(SELECT sum(order_fills.quant * order_fills.price)
FROM order_fills
WHERE `type`='closed'
AND order_fills.transaction_id = transactions.id) -
(SELECT sum(order_fills.quant * order_fills.price)
FROM order_fills
WHERE `type`='opened'
AND order_fills.transaction_id = transactions.id)) AS avgProfit,
SUM(
(SELECT sum(order_fills.quant * order_fills.price)
FROM order_fills
WHERE `type`='closed'
AND order_fills.transaction_id = transactions.id)) AS volume,
FROM transactions
WHERE transactions.status='closed';
to make the code more readable I wish to set some parts of that query into variables.
I wish to achieve something like that (it does not work, returns null):
SET #transSells =
(SELECT sum(order_fills.quant * order_fills.price)
FROM order_fills
WHERE type='closed'
AND order_fills.transaction_id = transactions.id);
SET #transBuys =
(SELECT sum(order_fills.quant * order_fills.price)
FROM order_fills
WHERE type='opened'
AND order_fills.transaction_id = transactions.id);
SELECT avg(#transSells - #transBuys) AS `avgProfit`,
sum(#transSells + #transBuys) AS `volume`
FROM transactions WHERE transactions.status='closed'
I think the problem might be that part of subquery WHERE statement is the result of the main query and another table (transactions.id):
AND order_fills.transaction_id = transactions.id
The reason your variable query doesn't work is that transactions.id in the WHERE clause of your variable definitions is not defined (NULL), hence the queries won't return any results.
I think you can simplify your query by putting the variables into a derived table using conditional aggregation that you then JOIN to:
SELECT AVG(transSells - transBuys) AS avgProfit,
SUM(transSells + transBuys) AS volume
FROM transactions
JOIN (SELECT transaction_id,
SUM(CASE WHEN type='closed' THEN quant * price ELSE 0 END) AS transSells,
SUM(CASE WHEN type='opened' THEN quant * price ELSE 0 END) AS transBuys
FROM order_fills) o ON o.transaction_id = transactions.id
WHERE status = 'closed'
I found a slightly better solution for my case:
setting variables inside the query, and then using them in the same, single query.
SET #comission = 0.03;
SELECT count(*),
AVG(
( #transSells := (SELECT sum(order_fills.quant * order_fills.price)
FROM order_fills
WHERE `type`='closed'
AND order_fills.transaction_id = transactions.id))
-
( #transBuys := (SELECT sum(order_fills.quant * order_fills.price)
FROM order_fills
WHERE `type`='opened'
AND order_fills.transaction_id = transactions.id))) AS avgProfit,
SUM( #transSells ) AS volume,
SUM( #transSells * #comission )
FROM transactions
WHERE transactions.status='closed';

MySQL Count on grouped by subqueries

I'm trying to make a SQL query on some flights (called legs). The goal is to know how many flights are on time, how many have between 5 and 15 mins of delay and how many more than 15.
Using subqueries, I managed to get a first result :
SELECT
count(*) as 'leg total',
leg2.l2count as 'less than 15min delay',
round(leg2.l2count / count(*) * 100, 2) as 'less than 15min delay percentage',
leg3.l3count as 'more than 15min delay',
round(leg3.l3count / count(*) * 100, 2) as 'more than 15min delay percentage',
count(*) - leg2.l2count - leg3.l3count as 'on time',
round((count(*) - leg2.l2count - leg3.l3count) / count(*) * 100, 2) as 'on time percentage'
FROM TBL_leg leg -- previsional dates
JOIN TBL_flight_reports fr ON leg.id = fr.leg_ops_id, -- realized dates
(
SELECT count(l2.id) as l2count
FROM TBL_leg l2
JOIN TBL_flight_reports fr2 ON l2.id = fr2.leg_ops_id
WHERE (
timediff(fr2.arrival_date_block, l2.to_date_time) > SEC_TO_TIME(5*60) AND
timediff(fr2.arrival_date_block, l2.to_date_time) < SEC_TO_TIME(15*60)
)
OR (
timediff(fr2.departure_date_block, l2.start_date_time) > SEC_TO_TIME(5*60) AND
timediff(fr2.departure_date_block, l2.start_date_time) < SEC_TO_TIME(15*60)
)
) leg2, -- delays between threshold and 15min
(
SELECT count(l3.id) as l3count
FROM TBL_leg l3
JOIN TBL_flight_reports fr3 ON l3.id = fr3.leg_ops_id
WHERE (
timediff(fr3.arrival_date_block, l3.to_date_time) > SEC_TO_TIME(15*60)
)
OR (
timediff(fr3.departure_date_block, l3.start_date_time) > SEC_TO_TIME(15*60)
)
) leg3 -- delays of more than 15min
;
This query seems to be fine.
Now, i'm trying to sort my results : in my TBL_leg table, there is an airplane_id column and I want to group my results by it. The goal being to have my delays by airplane.
The issue is getting my grouping in my subqueries. I'm missing some logic right there.
Also, if you have some better solution for my main issue (my current query), feel free :)
Regards
Make sure each sub query is grouped by the airplane id and add the airplane id to the select of the sub query and then join by the airplane id.
e.g. 2nd leg:
left join (
SELECT l2.airplane_id, count(l2.id) as l2count
FROM TBL_leg l2
JOIN TBL_flight_reports fr2 ON l2.id = fr2.leg_ops_id
WHERE (
timediff(fr2.arrival_date_block, l2.to_date_time) > SEC_TO_TIME(5*60) AND
timediff(fr2.arrival_date_block, l2.to_date_time) < SEC_TO_TIME(15*60)
)
OR (
timediff(fr2.departure_date_block, l2.start_date_time) > SEC_TO_TIME(5*60) AND
timediff(fr2.departure_date_block, l2.start_date_time) < SEC_TO_TIME(15*60)
)
GROUP BY l2.airplane_id
) leg2 ON leg.airplane_id = leg2.airplane_id
Group the total query on airplane_id as well.

DB2 Show the latest row record from users, using the date and time

I have 100 records from 3 users. I want to show the most recent record from each user. I have the following query:
SELECT *
FROM Mytable
WHERE Dateabc = CURRENT DATE
AND timeabc =
(
SELECT MAX(timeabc)
FROM Mytable
)
It returns the most recent record for everyone, and I need it to return most recent record from every user.
Should the solution support both DB2 and mysql?
SELECT * FROM Mytable as x
WHERE Dateabc = CURRENT_DATE
AND timeabc = (SELECT MAX( timeabc ) FROM Mytable as y where x.user = y.user)
If it's only DB2 more efficient solutions exists:
SELECT * from (
SELECT x.*, row_number() over (partition by user order by timeabc desc) as rn
FROM Mytable as x
)
WHERE rn = 1
I assume somewhere in your table you have a userID...
select userID, max(timeabc) from mytable group by userID
SELECT *
FROM Mytable as a
WHERE Dateabc = CURRENT_DATE
AND timeabc =
(
SELECT MAX( timeabc )
FROM Mytable as b
WHERE a.uId = b.uId
)

Optimize SQL Server Query

I have to do a query to get the total cost of previous month and compared to current month to calculate the percentage difference.
this is the script:
create table #calc
(
InvoiceDate Date,
TotalCost decimal (12,2)
)
insert into #calc values ('2013-07-01', 9470.36)
insert into #calc values ('2013-08-01', 11393.81)
and this is the query:
select InvoiceDate,
TotalCost,
PrevTotalCost,
(CASE WHEN (PrevTotalCost = 0)
THEN 0
ELSE (((TotalCost - PrevTotalCost) / PrevTotalCost) * 100.0)
END) AS PercentageDifference
from (
select a.InvoiceDate, a.TotalCost,
isnull((select b.TotalCost
from #calc b
where InvoiceDate = (select MAX(InvoiceDate)
from #calc c
where c.InvoiceDate < a.InvoiceDate)), 0) as PrevTotalCost
from #calc a) subq
Is there a more efficient way to do it for cgetting the previous month?
Using a ranking function to put more burden on sorts than table scans seems the fastest when using no indexes. The query below processed 6575 records in under a second:
SELECT
Main.InvoiceDate,
Main.TotalCost,
PreviousTotalCost=Previous.TotalCost,
PercentageDifference=
CASE WHEN COALESCE(Previous.TotalCost,0) = 0 THEN 0
ELSE (((Main.TotalCost - Previous.TotalCost) / Previous.TotalCost) * 100.00)
END
FROM
(
SELECT
InvoiceDate,
TotalCost,
OrderInGroup=ROW_NUMBER() OVER (ORDER BY InvoiceDate DESC)
FROM
Test
)AS Main
LEFT OUTER JOIN
(
SELECT
InvoiceDate,
TotalCost,
OrderInGroup=ROW_NUMBER() OVER (ORDER BY InvoiceDate DESC)
FROM
Test
)AS Previous ON Previous.OrderInGroup=Main.OrderInGroup+1
Using nested looping as the case when getting the previous invoice cost in a select subquery proves the slowest - 6575 rows in 30 seconds.
SELECT
X.InvoiceDate,
X.TotalCost,
X.PreviousTotalCost,
PercentageDifference=
CASE WHEN COALESCE(X.PreviousTotalCost,0) = 0 THEN 0
ELSE (((X.TotalCost - X.PreviousTotalCost) / X.PreviousTotalCost) * 100.00)
END
FROM
(
SELECT
InvoiceDate,
TotalCost,
PreviousTotalCost=(SELECT TotalCost FROM Test WHERE InvoiceDate=(SELECT MAX(InvoiceDate) FROM Test WHERE InvoiceDate<Main.InvoiceDate))
FROM
Test AS Main
)AS X
Your query processed 6575 records in 20 seconds with the biggest cost coming from the nested loops for inner join
select InvoiceDate,
TotalCost,
PrevTotalCost,
(CASE WHEN (PrevTotalCost = 0)
THEN 0
ELSE (((TotalCost - PrevTotalCost) / PrevTotalCost) * 100.0)
END) AS PercentageDifference
from (
select a.InvoiceDate, a.TotalCost,
isnull((select b.TotalCost
from Test b
where InvoiceDate = (select MAX(InvoiceDate)
from #calc c
where c.InvoiceDate < a.InvoiceDate)), 0) as PrevTotalCost
from Test a) subq
Using indexes would be a big plus unless you are required to use temp tables.
Hope this helps :)
SELECT
`current`.`InvoiceDate`,
`current`.`TotalCost`,
`prev`.`TotalCost` AS `PrevTotalCost`,
(`current`.`TotalCost` - `prev`.`TotalCost`) AS `CostDifference`
FROM dates `current`
LEFT JOIN
dates `prev`
ON `prev`.`InvoiceDate` <= DATE_FORMAT(`current`.`InvoiceDate` - INTERVAL 1 MONTH, '%Y-%m-01');
Screenshot of the results I got: http://cl.ly/image/0b3z2x1f2H1n
I think this might be what you're looking for.
Edit: I wrote this query in MySQL, so it's possible you may need to alter a couple minor syntax things for your server.

calculate percentage with subqueries

I'm trying to calculate a percentage in my SQL query.
This is what I have right now:
SELECT
DATE(processed_at) AS day,
(
SELECT
COUNT(*) FROM return_items
WHERE return_id IN (SELECT id FROM returns WHERE DATE(processed_at) = day)
) as products_returned,
COUNT(*) as return_count,
(
SELECT
COUNT(*) as co_returns
FROM returns
WHERE return_method = 'mondial_relais'
AND DATE(processed_at) = day
) as return_rate_mr
FROM returns
WHERE MONTH(processed_at) = 10
AND YEAR(processed_at) = 2011
GROUP BY day;
Basically I need the return_rate_mr to be a percentage value.
I tried doing something like return_rate_mr * 100 / return_count as perc_value but this doesn't work. (I don't actually need the current return_rate_mr value, just the percentage.
Any ideas?
Assuming your original query returns the desired results, you can wrap it as a subquery:
SELECT
day,
return_rate_mr * 100 / return_count as perc_value,
... any other columns ...
FROM
( ... your original query here ...) as myalias;
Basically, the subquery creates a result set where the columns are renamed. Then, the outer query is free to use those new column names.
Are you looking for this?
SELECT
`day`,
`products_returned`,
(`return_rate_mr` * 100) / `return_count` AS `percentage`
FROM (
SELECT
DATE(processed_at) AS day,
(
SELECT
COUNT(*) FROM return_items
WHERE return_id IN (SELECT id FROM returns WHERE DATE(processed_at) = day)
) as products_returned,
COUNT(*) as return_count,
(
SELECT
COUNT(*) as co_returns
FROM returns
WHERE return_method = 'mondial_relais'
AND DATE(processed_at) = day
) as return_rate_mr
FROM returns
WHERE MONTH(processed_at) = 10
AND YEAR(processed_at) = 2011
GROUP BY day) AS `ss`
Did you try something like:
SELECT (`return_rate_mr` * 100 ) / `return_count` as "yourValue", OthersFields
FROM SELECT
DATE(processed_at) AS day,
(
SELECT
COUNT(*) FROM return_items
WHERE return_id IN (SELECT id FROM returns WHERE DATE(processed_at) = day)
) as products_returned,
COUNT(*) as return_count,
(
SELECT
COUNT(*) as co_returns
FROM returns
WHERE return_method = 'mondial_relais'
AND DATE(processed_at) = day
) as return_rate_mr
FROM returns
WHERE MONTH(processed_at) = 10
AND YEAR(processed_at) = 2011
GROUP BY day;
Hope this helps
Try:
SELECT DATE(processed_at) AS day,
count(distinct id) as products_returned,
COUNT(*) as return_count,
100* sum(case return_method when 'mondial_relais' then 1 end) / COUNT(*)
as return_perc_mr
FROM returns
WHERE MONTH(processed_at) = 10
AND YEAR(processed_at) = 2011
GROUP BY day;
I suspect that products_returned should be counting distinct item_id values (or something similar), but this should duplicate the logic in the original query.