Mysql - Multiple Sum Functions in query - mysql

I have a query that has several sum functions that are the same. Would it be more efficient to run a separate sum query then bind it to my original query as a value to speed it up?
My current query
$dbquery = $dbh->prepare("
SELECT amount - (Select SUM(amountused) from tblamountused) as amountleft
FROM tblamount
WHERE amount - (Select SUM(amountused) from tblamountused) > 0
");

Maybe something like that:
SELECT amount - (Select SUM(amountused) from tblamountused) as amountleft
FROM tblamount
HAVING amountleft > 0

Related

Create a MySQL view to Get Average for Count Column

I need to create a view to get the sum of the count column and display the average as a new column. I used the below code.
select
count(`t`.`surg_priority`) AS `Surgery_Count`,
`t`.`surg_priority` AS `Surgery_Type`
from
`DataBase`.`booking` t
group by
`t`.`surg_priority
This was the result. I need a new column called average to get the total of surgery Avg.
Surgery Average = (Surgery Count / Sum of Surgery Count) * 100
I also tried
select
count(`t`.`surg_priority`) AS `Surgery_Count`,
`t`.`surg_priority` AS `Surgery_Type`,
(
(count(`t`.`surg_priority`)/(sum(Surgery_Count))
)* 100 AS `Surgery_AVG`
from
`DataBase`.`orbkn_booking` t
group by
`t`.`surg_priority`
This too didn't work. Make sure this is a view. Cant use variable or cumulative functions
You can compute the total count in a subquery and divide the Surgery_Count by that:
select
count(`t`.`surg_priority`) AS `Surgery_Count`,
`t`.`surg_priority` AS `Surgery_Type`,
100.0 * count(`t`.`surg_priority`) /
(select count(`surg_priority`) from `DataBase`.`booking`) AS `Surgery_Avg`
from
`DataBase`.`booking` t
group by
`t`.`surg_priority
Use window functions:
select b.surg_priority as Surgery_Type, count(*) as surgery_count,
count(*) * 100.0 / sum(count(*)) over () as ratio
from DataBase.booking b
group by b.surg_priority;
These have been available since MySQL 8+ was released.
Also, don't clutter your queries with backticks. They just make queries harder to write and read.

Is there any way in SQL or function in MYSQL that sums up all the increments in a column?

I want to find a way to sum up all the increments in the value of a column.
We provide delivery services to our customers. A customer can pay as he go, but if he pays an upfront fee, he gets a better deal. There is a table that has the balance of the customer across the time. So I want to sum all the increments to the balance. I can't change the way the payment is recorded.
I have alredy coded an stored procedure that works, but is kind slow, so I'm looking for alternatives. I think that, maybe, an sql statement that can do this task, can outperform my stored procedure that has loops.
My stored procedure makes a select of the customer in a given date range, and insert the result in a temp table X. After that, it starts to pop rows from X table, comparing the balance value in that row against the previous row, and detects if there is an increment. If there is not increment, pops another row and do the same routine, if there is an increment, it calculates the difference between that row and the previous, and the result is inserted in another temp table Y.
When there are no rows left, the stored procedure performs a SUM in the temp table Y, and thus, you can know how much the customer has "refilled" its balance.
This is an example of the table X, and the expected result:
DATE BALANCE
---- -------
2019-02-01 200
2019-02-02 195 //from 200 to 195 there is a decrement, so it doesn't matter
2019-02-03 180
2019-02-04 150
2019-02-05 175 //there is an increment from 150 to 175, it's 25 that must be inserted in the temp table
2019-02-06 140
2019-02-07 180 //there is another increment, from 140 to 180, it's 40
So the resulting temp table Y must be something like this:
REFILL
------
25
40
The expected result is 65. My stored procedure returns this value, but as I said, is kind slow (it takes about 22 seconds to process 3900 rows, equivalent to 3 days, aprox), I think is because the loops. I would like to explore another alternatives. Because some details that I don't mention here, for a single costumer, I can have 1300 rows per day (the example is given in days, but I have rows by the minute). My tables are indexed, I think properly. I can't post my stored procedure, but it works as described (I know that "The devil is in the detail"). So any suggestion will be appreciated.
Use a user-defined variable to hold the balance from the previous row, and then subtract it from the current row's balance.
SELECT SUM(refill) AS total_refill
FROM (
SELECT GREATEST(0, balance - #prev_balance) AS refill, #prev_balance := balance
FROM (
SELECT balance
FROM tableX
ORDER BY date) AS t
CROSS JOIN (SELECT #prev_balance := NULL) AS ars
) AS t
There is a quite well-known mechanism to deal with these: Use a variable inside a field.
SELECT #result:=0;
SELECT #lastbalance:=9999999999; -- whatever value is sure to be highe than any real balance
SELECT SUM(increments) AS total FROM (
SELECT
IF(balance>#lastbalance, balance-#lastbalance, 0) AS increments,
#lastbalance:=balance AS ignore
FROM X -- insert real table name here
WHERE
-- insert selector here
ORDER BY
-- insert real chronological sorter here
) AS baseview;
Use lag() in MySQL 8+:
select sum(balance - prev_balance) as refills
from (select t.*, lag(balance) over (order by date) prev_balance
from t
) t
where balance > prev_balance;
In older versions of MySQL this is tricky. If the values are continuous dates, then a simple JOIN works:
select sum(t.balance - tprev.balance) as refills
from t join
t tprev
on tprev.date = t.date - 1
where t.balance > tprev.balance;
This may not be the case. Then the next best method is variables. But you have to be very careful. MySQL does not declare the order of evaluation of expressions in a SELECT. As the documentation explains:
The order of evaluation for expressions involving user variables is undefined. For example, there is no guarantee that SELECT #a, #a:=#a+1 evaluates #a first and then performs the assignment.
The variables need to be assigned and used in the same expression:
select sum(balance - prev_balance) as refills
from (select t.*,
(case when (#temp_prevb := #prevb) = NULL -- intentionally false
then -1
when (#prevb := balance)
then #temp_prevb
end) as prev_balance
from (select t.* from t order by date) t cross join
(select #prevb := NULL) params
) t
where balance > prev_balance;
And the final method is a correlated subquery:
select sum(balance - prev_balance) as refills
from (select t.*,
(select t2.balance
from t t2
where t2.date < t.date
order by t2.date desc
) as prev_balance
from t
) t
where balance > prev_balance;

How to use max for two tables

I want use MAX on a time field, it works but the value is "0"
This query is before I use MAX:
SELECT a.`JAM`, AVG(a.PacketLoss) AVG
FROM (
SELECT `JAM`,`RNC`,`IPPATH_PM` AS PHB,`VS_IPPM_FORWORD_DROPMEANS` AS PacketLoss, `VSIPPMRttMeans` AS Latency, 'IPPM'
FROM `rnc_ippm_meas`
WHERE `JAM`>= CURDATE() AND `IPPATH_PM` LIKE '%AF31%'
UNION
SELECT `JAM`,`RNC`,`IPPOOL_PM`,`VSIPPOOLIPPMForwardDrop-Means`,`VSIPPOOLIPPMRttMeans`, 'IPPOOLPM'
FROM `rnc_ippool_ippm_meas`
WHERE `JAM`>= CURDATE() AND `IPPOOL_PM` LIKE '%AF31%') a
GROUP BY a.`JAM`
LIMIT 10000
output here
output1
Then I use MAX here
SELECT a.`JAM`, AVG(a.PacketLoss) AVG
FROM (
SELECT MAX(JAM) AS JAM,`RNC`,`IPPATH_PM` AS PHB,`VS_IPPM_FORWORD_DROPMEANS` AS PacketLoss, `VSIPPMRttMeans` AS Latency, 'IPPM'
FROM `rnc_ippm_meas`
WHERE `JAM`>= CURDATE() AND `IPPATH_PM` LIKE '%AF31%'
UNION
SELECT MAX(JAM) AS JAM,`RNC`,`IPPOOL_PM`,`VSIPPOOLIPPMForwardDrop-Means`,`VSIPPOOLIPPMRttMeans`, 'IPPOOLPM'
FROM `rnc_ippool_ippm_meas`
WHERE `JAM`>= CURDATE() AND `IPPOOL_PM` LIKE '%AF31%') a
GROUP BY a.`JAM`
LIMIT 10000
output2
I expected the output is last time with the value, can someone help me ? Thanks
You need to take the MAX on your outer query, not the inner one. When you take it on the inner queries, MySQL gives you an indeterminate value of the non-aggregated columns e.g. VS_IPPM_FORWORD_DROPMEANS, which appears to be a 0 value for your sample data. Change your query to:
SELECT MAX(a.`JAM`), AVG(a.PacketLoss) AVG
FROM (
SELECT `JAM`,`RNC`,`IPPATH_PM` AS PHB,`VS_IPPM_FORWORD_DROPMEANS` AS PacketLoss, `VSIPPMRttMeans` AS Latency, 'IPPM'
FROM `rnc_ippm_meas`
WHERE `JAM`>= CURDATE() AND `IPPATH_PM` LIKE '%AF31%'
UNION
SELECT `JAM`,`RNC`,`IPPOOL_PM`,`VSIPPOOLIPPMForwardDrop-Means`,`VSIPPOOLIPPMRttMeans`, 'IPPOOLPM'
FROM `rnc_ippool_ippm_meas`
WHERE `JAM`>= CURDATE() AND `IPPOOL_PM` LIKE '%AF31%') a
Note the GROUP BY is no longer required since you are taking the MAX and the AVG over the whole data set, and the LIMIT is not required as this query will only produce 1 row.

query optimization with multiple sub-queries

I want to retrieve data
number of meters in this month, minus the number of meters in the previous month,
and the value of the meter is deducted in accordance with their respective codes.
then summed the whole.
there are about 8000 records.
but I try to take 5 records, and it takes time 2:53 sec,
100 records takes time (1 min 1:57 sec).
really matter .
I have query like this.
SELECT code hvCode,
IFNULL( (SELECT meter
FROM bmrpt
WHERE waktu_foto LIKE '2014-05%'
GROUP BY code HAVING code = hvCode),0 )
-IFNULL( (SELECT meter
FROM bmrpt WHERE waktu_foto LIKE '2014-04%'
GROUP BY code HAVING code = hvCode),0 )hasil
FROM bmrpt group by code;
does anybody have an idea to change the query to be optimized?
this the sqlfiddle http://www.sqlfiddle.com/#!2/495c0/1
best regards
though your question is unclear but try below subquery , as what I understand
SELECT COALESCE(SUM(`meter`),0) FROM table WHERE code ='hvCode' AND MONTH(`date_column`) = 5
-
SELECT COALESCE(SUM(`meter`),0) FROM table WHERE code ='hvCode' AND MONTH(`date_column`) = 4

Combine multiple room availability queries into one

I'm currently trying to optimize an database by combining queries. But I keep hitting dead ends while optimizing an room availability query.
I have a room availability table where each records states the available number of rooms per date. It's formatted like so:
room_availability_id (PK)
room_availability_rid (fk_room_id)
room_availability_date (2011-02-11)
room_availability_number (number of rooms available)
The trouble is getting a list of rooms that are available for EACH of the provided days. When I use IN() like so:
WHERE room_availability_date IN('2011-02-13','2011-02-14','2011-02-15')
AND room_availability_number > 0
If the 14th has availability 0 it still gives me the other 2 dates. But I only want that room_id when it is available on ALL three dates.
Please tell me there is a way to do this in MySQL other than querying each date/room/availability combination separately (that is what is done now :-( )
I tried all sorts of combinations, tried to use room_availability_date = ALL (...), tried some dirty repeating subqueries but to no avail.
Thank you in advance for any thoughts!
You would need to construct a query to group on the room ID and then check that there is availability on each date, which can be done using the having clause. Leaving the where clause predicate in for room_availability_date will help to keep the query efficient (as indexes etc. can't be used with a having clause easily).
SELECT
room_availability_rid
WHERE room_availability_date IN ('2011-02-13','2011-02-14','2011-02-15')
AND room_availability_number > 0
GROUP BY room_availability_rid
HAVING count(case room_availability_date when '2011-02-13' THEN 1 END) > 0
AND count(case room_availability_date when '2011-02-14' THEN 1 END) > 0
AND count(case room_availability_date when '2011-02-15' THEN 1 END) > 0
I think I can improve on a'r's answer:
SELECT
room_availability_rid, count(*) n
WHERE room_availability_date IN ('2011-02-13','2011-02-14','2011-02-15')
AND room_availability_number > 0
GROUP BY room_availability_rid
HAVING n=3
Edit: This of course assumes that there is only one table entry per room per day. Is this a valid assumption?
You can group by room ID, generate a list of dates available, and then see if all the dates you need are included.
This will give you a list of dates each room is available:
select `room_availability_rid`,group_concat(`room_ availability_date`) as `datelist`
from `table` where room_availability_number>0
group by `room_availability_rid`
Then we can add a having clause to get the rooms that are available on all of the dates we need:
select `room_availability_rid`,group_concat(`room_ availability_date`) as `datelist`
from `table` where room_availability_number>0
group by `room_availability_rid`
having find_in_set('2011-02-13',`datelist`) and
find_in_set('2011-02-14',`datelist`) and
find_in_set('2011-02-15',`datelist`)
This should work. Test it for me will ya? :)