Can I optimize this UNION query? [duplicate] - mysql

This question already has answers here:
How do I create an SQL query that groups by value ranges
(7 answers)
Closed 1 year ago.
Hello I'm taking some data from my db and I want to get this output:
How many times I have an amount less then 100€ (inbound, outbound and inner)
How many times I have an amount between 100 and 500€ (inbound, outbound and inner)
How many times I have an amount between 500 and 2000€ (inbound, outbound and inner)
...
How many times I have an amount greater then 200000€ (inbound, outbound and inner)
I can obtain this with this query:
SELECT
'< 100 €' as amount,
SUM(CASE WHEN direction = 'inbound' AND to_address is null THEN 1 ELSE 0 END) AS inbound,
SUM(CASE WHEN direction = 'outbound' AND to_address is null THEN 1 ELSE 0 END) AS outbound,
SUM(CASE WHEN to_address is not null THEN 1 ELSE 0 END) AS inside
FROM user_wallet_movement
WHERE
(amount*base_rate) < 100
AND status = 'execute'
AND mov_date > '2020-01-07'
AND mov_date < '2021-06-30'
UNION
SELECT
'100 ... 500 €' as amount,
SUM(CASE WHEN direction = 'inbound' AND to_address is null THEN 1 ELSE 0 END) AS inbound,
SUM(CASE WHEN direction = 'outbound' AND to_address is null THEN 1 ELSE 0 END) AS outbound,
SUM(CASE WHEN to_address is not null THEN 1 ELSE 0 END) AS inside
FROM user_wallet_movement
WHERE
(amount*base_rate) > 100 AND (amount*base_rate) < 500
AND status = 'execute'
AND mov_date > '2020-01-07'
AND mov_date < '2021-06-30'
UNION
SELECT
'500 ... 2000 €' as amount,
SUM(CASE WHEN direction = 'inbound' AND to_address is null THEN 1 ELSE 0 END) AS inbound,
SUM(CASE WHEN direction = 'outbound' AND to_address is null THEN 1 ELSE 0 END) AS outbound,
SUM(CASE WHEN to_address is not null THEN 1 ELSE 0 END) AS inside
FROM user_wallet_movement
WHERE
(amount*base_rate) > 500 AND (amount*base_rate) < 2000
AND status = 'execute'
AND mov_date > '2020-01-07'
AND mov_date < '2021-06-30'
UNION
SELECT
'2000 ... 10000 €' as amount,
SUM(CASE WHEN direction = 'inbound' AND to_address is null THEN 1 ELSE 0 END) AS inbound,
SUM(CASE WHEN direction = 'outbound' AND to_address is null THEN 1 ELSE 0 END) AS outbound,
SUM(CASE WHEN to_address is not null THEN 1 ELSE 0 END) AS inside
FROM user_wallet_movement
WHERE
(amount*base_rate) > 2000 AND (amount*base_rate) < 10000
AND status = 'execute'
AND mov_date > '2020-01-07'
AND mov_date < '2021-06-30'
UNION
SELECT
'10000 ... 50000 €' as amount,
SUM(CASE WHEN direction = 'inbound' AND to_address is null THEN 1 ELSE 0 END) AS inbound,
SUM(CASE WHEN direction = 'outbound' AND to_address is null THEN 1 ELSE 0 END) AS outbound,
SUM(CASE WHEN to_address is not null THEN 1 ELSE 0 END) AS inside
FROM user_wallet_movement
WHERE
(amount*base_rate) > 10000 AND (amount*base_rate) < 50000
AND status = 'execute'
AND mov_date > '2020-01-07'
AND mov_date < '2021-06-30'
UNION
SELECT
'50000 ... 200000 €' as amount,
SUM(CASE WHEN direction = 'inbound' AND to_address is null THEN 1 ELSE 0 END) AS inbound,
SUM(CASE WHEN direction = 'outbound' AND to_address is null THEN 1 ELSE 0 END) AS outbound,
SUM(CASE WHEN to_address is not null THEN 1 ELSE 0 END) AS inside
FROM user_wallet_movement
WHERE
(amount*base_rate) > 50000 AND (amount*base_rate) < 200000
AND status = 'execute'
AND mov_date > '2020-01-07'
AND mov_date < '2021-06-30'
UNION
SELECT
'> 200000 €' as amount,
SUM(CASE WHEN direction = 'inbound' AND to_address is null THEN 1 ELSE 0 END) AS inbound,
SUM(CASE WHEN direction = 'outbound' AND to_address is null THEN 1 ELSE 0 END) AS outbound,
SUM(CASE WHEN to_address is not null THEN 1 ELSE 0 END) AS inside
FROM user_wallet_movement
WHERE
(amount*base_rate) > 200000
AND status = 'execute'
AND mov_date > '2020-01-07'
AND mov_date < '2021-06-30'
As you can see the conditions are all the same, the only thing that changes is the condition on (amount*base_rate).
Is there a way to optimize this?
Thank you

Thanks to #user202729's comment I got the solution
SELECT
Conds.Description,
SUM(CASE WHEN direction = 'inbound' AND to_address is null THEN 1 ELSE 0 END) AS inbound,
SUM(CASE WHEN direction = 'outbound' AND to_address is null THEN 1 ELSE 0 END) AS outbound,
SUM(CASE WHEN to_address is not null THEN 1 ELSE 0 END) AS inside
FROM user_wallet_movement uwm
JOIN (
SELECT '< 100' AS Description, 0 AS min, 100 AS max
UNION ALL
SELECT '100 ... 500' AS Description, 100 AS min, 500 AS max
UNION ALL
SELECT '500 ... 2000' AS Description, 500 AS min, 2000 AS max
UNION ALL
SELECT '2000 ... 10000' AS Description, 2000 AS min, 10000 AS max
UNION ALL
SELECT '10000 ... 50000' AS Description, 10000 AS min, 50000 AS max
UNION ALL
SELECT '50000 ... 200000' AS Description, 50000 AS min, 200000 AS max
UNION ALL
SELECT '> 200000' AS Description, 200000 AS min, 9999999999 AS max
) as Conds ON (uwm.amount*uwm.base_rate) >= min AND (uwm.amount*uwm.base_rate) < Conds.max
WHERE uwm.status = 'execute'
AND uwm.mov_date > '2020-01-07'
AND uwm.mov_date < '2021-06-30'
GROUP BY Conds.Description

If this is going to be a frequent query and the date range is likely to change, then build and maintain a summary table.
Such a table would have (perhaps) one row per day and status. The columns would be counts for each amount range.
Each night, update the table with the previous day's counts.
Then write the query to reach into the summary table; it will be a lot faster.
More: http://mysql.rjweb.org/doc.php/summarytables

Related

Need help in Merging Two query for HP ALM

I have two different query in HP ALM, but i want to merge and get it into one. I am not that good in SQL query so I am facing hard time in merging the query.
Query 1: Getting the execution Count for the tester
Select
TESTCYCL.TC_ACTUAL_TESTER as 'Tester',
sum(case when TC_Status In('Blocked','Passed','Failed','Not Completed') then 1 else 0 end) as 'Total',
sum(case when TC_Status = 'Passed' then 1 else 0 end) as 'Pass',
sum(case when TC_Status = 'Failed' then 1 else 0 end) as 'Fail',
sum(case when TC_Status = 'Blocked' then 1 else 0 end) as 'Blocked',
sum(case when TC_Status In('Not Completed','Defferred','N/A') then 1 else 0 end) as 'Others'
From TESTCYCL
Where
TESTCYCL.TC_EXEC_DATE = CAST(CURRENT_TIMESTAMP AS DATE)
And
TESTCYCL.TC_ACTUAL_TESTER in ('Username1')
Group by TC_ACTUAL_TESTER
Query 2: Getting the Defect raise by the tester
SELECT
BG_DETECTED_BY,
Sum(case when BG_Status Not in ('Closed','Defect Resolved','Rejected')then 1 else 0 end) as 'Defect Raised'
FROM BUG
Where BUG.BG_DETECTED_BY in ('username1')
AND BUG.BG_DETECTION_DATE = CAST(CURRENT_TIMESTAMP AS DATE)
Group by BG_DETECTED_BY
I have tried inner join/ Left Join but the count of the defect raised by the user is not matching
Query3 : That i have tried:
Select
TESTCYCL.TC_ACTUAL_TESTER as 'Tester',
sum(case when TC_Status In('Blocked','Passed','Failed','Not Completed') then 1 else 0 end) as 'Total',
sum(case when TC_Status = 'Passed' then 1 else 0 end) as 'Pass',
sum(case when TC_Status = 'Failed' then 1 else 0 end) as 'Fail',
sum(case when TC_Status = 'Blocked' then 1 else 0 end) as 'Blocked',
sum(case when TC_Status In('Not Completed','Defferred','N/A') then 1 else 0 end) as 'Others',
Sum(case when BG_Status Not in ('Closed','Defect Resolved','Rejected')then 1 else 0 end) as 'Defect Raised'
From TESTCYCL
Left Join BUG
on TESTCYCL.TC_ACTUAL_TESTER = BUG.BG_DETECTED_BY
AND BUG.BG_DETECTION_DATE = CAST(CURRENT_TIMESTAMP AS DATE)
Where TESTCYCL.TC_EXEC_DATE = CAST(CURRENT_TIMESTAMP AS DATE)
And TESTCYCL.TC_ACTUAL_TESTER in ('username1')
Group by TC_ACTUAL_TESTER
Out is mentioned as below:
Expected Output:
Tester Total Execution Passed Failed ... Defect Raised
A 5 3 2 10
Actual Output:
Tester Total Execution Passed Failed ... Defect Raised
A 56 3 2 45
The problem is you are doing the COUNT() over the product cartesian. instead of concatenating the result.
Right now you are doing
COUNT(A*B) instead of COUNT(A) || COUNT(B)
general example, if you have two queries
SELECT * FROM A (ex: 10 rows)
SELECT * FROM B (ex: 10 rows)
What you need is:
SELECT temp1.*, temp2.*
FROM (SELECT * FROM A) as temp1
JOIN (SELECT * FROM B) as temp2
ON temp1.ID = temp2.ID

Count each day with SQL query

I want to display a table with SQL query like this:
I have run my query:
select
tb_r_orderdata.finishtime as date ,
count(*)sum all,
sum(when status = 'SUCCESS' and issync = '1' then 1 else 0 end) sumpaid,
sum(when status = 'SUCCESS' and issync in ('3', '4') then 1 else 0 end) sumfail,
sum(when status = 'CLOSED' then 1 else 0 end) sumclose,
sum(when status = 'Null' then 1 else 0 end) sumunflag
from
tb_r_orderdata;
But when I execute it, the result is different than what I expected. The result is like this:
Thank you for any help
You are missing the GROUP BY and the CASE:
select tb_r_orderdata.finishtime as date ,
COUNT(*) as sumall,
SUM(CASE WHEN status='SUCCESS' AND issync='1' then 1 ELSE 0 END) as sumpaid,
SUM(CASE WHEN status='SUCCESS' AND issync in ('3','4') then 1 ELSE 0 END) as sumfail,
SUM(CASE WHEN status='CLOSED' then 1 ELSE 0 END) as sumclose,
SUM(CASE WHEN status is null then 1 ELSE 0 END) as sumunflag
from tb_r_orderdata
group by tb_r_orderdata.finishtime ;
MySQL treats booleans as integers in a numeric context, with "1" for true and "0" for false. You can simplify your query to:
select o.finishtime as date ,
COUNT(*) as sumall,
SUM(status = 'SUCCESS' AND issync = '1') as sumpaid,
SUM(status = 'SUCCESS' AND issync in ('3', '4')) as sumfail,
SUM(status = 'CLOSED') as sumclose,
SUM(status is null) as sumunflag
from tb_r_orderdata o
where tb_r_orderdata.finishtime is not NULL
group by o.finishtime ;
This also removes NULL finish times.
Explanation in comment for Gordon Linoff
i have try your answer but there is record NULL in the top of table, i dont know why, can you explain it
with little bit change from Gordon Linoff answer
select tb_r_orderdata.finishtime as date ,
SUM(CASE WHEN status='SUCCESS' AND issync='1' then 1 ELSE 0 END) as sumpaid,
SUM(CASE WHEN status='SUCCESS' AND issync in ('3','4') then 1 ELSE 0 END) as sumfail,
SUM(CASE WHEN status='CLOSED' then 1 ELSE 0 END) as sumclose,
SUM(CASE WHEN status='NULL' then 1 ELSE 0 END) as sumunflag
from tb_r_orderdata
group by tb_r_orderdata.finishtime

Calculate date difference in sql for specific year

I have two dates, say start_date is 20141215 and end_date = 20150115. I would like to use SQL DATEDIFF to only count the dates within the year 2015 which I will specify in the query. Here is the current SQL I have written:
SELECT COUNT(leave_id),
sum(case when leave_status = 1 then 1 else 0 end) pending,
sum(case when leave_status = 2 then 1 else 0 end) declined,
sum(case when leave_status = 3 then 1 else 0 end) approved,
sum(case when leave_status = 4 then 1 else 0 end) rostered,
SUM(DATEDIFF(end_date, start_date)+1) as datetotals
FROM employee_leave WHERE
((YEAR(start_date) = :year) OR (YEAR(end_date) = :year))
AND employee_id = :emp_id
Thanks
You need to fix datediff() to only consider dates during the year. I think this does what you want:
SELECT COUNT(leave_id),
sum(case when leave_status = 1 then 1 else 0 end) pending,
sum(case when leave_status = 2 then 1 else 0 end) declined,
sum(case when leave_status = 3 then 1 else 0 end) approved,
sum(case when leave_status = 4 then 1 else 0 end) rostered,
SUM(DATEDIFF(least(end_date, date(concat_ws('-', :year, 12, 31))),
greatest(start_date, date(concat_ws('-', :year, 1, 1)))
) + 1) as datetotals
FROM employee_leave
WHERE ((YEAR(start_date) = :year) OR (YEAR(end_date) = :year)) AND
employee_id = :emp_id
Make it a AND condition rather like
WHERE
((YEAR(start_date) = :year) AND (YEAR(end_date) = :year))

How could I check the value of an aggregation function inside the same query?

This MySQL query gives me this error 'Unknown column 'winnings' in 'field list'
SELECT
o.user_id,
sum(case when o.result = 1 or o.result=2 or o.result = 0 then 1 else 0 end) as tahmins_no,
sum(case when o.result = 1 then 1 else 0 end) as winnings,
sum(case when o.result = 2 then 1 else 0 end) as loses,
sum(case when winnings = 10 then 0.5 else 0 end) as counter
FROM `odds_tahminler` o
I know that winnings is the value of the sum() aggregation function, But is there any way to check the winnings value within the query?
You can't use an aggregated column inside the select. However you can use a subquery to obtain the counter value after all the aggregated columns have been computed.
How is the counter value calculated? I assumed that the counter should be (winnings - 10) / 2 if there's at least 10 winnings and 0 otherwise. In that case you can obtain it with this query
SELECT O.*,
GREATEST( (O.winnings - 10) / 2, 0) as counter
FROM
(
SELECT u.username,
o.user_id,
sum(case when o.result = 1 or o.result=2 or o.result = 0 then 1 else 0 end) as tahmins_no,
sum(case when o.result = 1 then 1 else 0 end) as winnings,
sum(case when o.result = 2 then 1 else 0 end) as loses
FROM `odds_tahminler` o
) as O
you can try:
SELECT
o.user_id,
sum(case when o.result = 1 or o.result=2 or o.result = 0 then 1 else 0 end) as tahmins_no,
sum(case when o.result = 1 then 1 else 0 end) as winnings,
sum(case when o.result = 2 then 1 else 0 end) as loses,
sum(case when winnings = 10 then 0.5 else 0 end) as counter
FROM `odds_tahminler` o
GROUP BY o.user_id
HAVING counter>2

SUM for more fields in mysql

I have a query,which give me wrong result.Here is my query.
SELECT sum(open1) open1, sum(closed1) closed,sum(pending1) NotSpecified,
status,type,sub_status,created_date,bystatus,lst_type
FROM (
SELECT agent_1_id,status,type,sub_status,created_date,'Open' as bystatus,
(CASE WHEN type = 1 THEN 'Rent'
WHEN type = 2 THEN 'Sale'
ELSE 'Not Specified' END) as lst_type,
count(*) as open1, 0 closed1, 0 pending1
FROM crm_mydeals
where status = 1 AND agent_1_id>0 and is_active=1
GROUP BY status
UNION ALL
SELECT agent_1_id,status,type,sub_status,created_date,'Closed' as bystatus,
(CASE WHEN type = 1 THEN 'Rent'
WHEN type = 2 THEN 'Sale'
ELSE 'Not Specified' END) as lst_type,
0 open1, count(*) as closed1, 0 pending1
FROM crm_mydeals
where status = 2 AND agent_1_id>0 and is_active=1
GROUP BY status
UNION ALL
SELECT agent_1_id,status,type,sub_status,created_date,'NotSpecified' as bystatus,
(CASE WHEN type = 1 THEN 'Rent'
WHEN type = 2 THEN 'Sale'
ELSE 'Not Specified' END) as lst_type,
0 open1, 0 closed1, count(*) as pending1
FROM crm_mydeals
where status = 3 AND agent_1_id>0 and is_active=1
GROUP BY status
) s
WHERE DATE(created_date) BETWEEN '2013-11-22' AND '2014-2-22'
GROUP BY status
If you simplify the query, it might help to find the correct answer?
SELECT SUM(CASE WHEN status = 1 THEN 1 ELSE 0) AS open1,
SUM(CASE WHEN status = 2 THEN 1 ELSE 0) AS closed,
SUM(CASE WHEN Status = 3 THEN 1 ELSE 0) AS NotSpecified,
status,
type,
sub_status,
created_date,
(CASE WHEN status = 1 THEN 'Open'
WHEN status = 2 THEN 'Closed'
WHEN status = 3 THEN 'NotSpecified') AS bystatus,
(CASE WHEN type = 1 THEN 'Rent'
WHEN type = 2 THEN 'Sale'
ELSE 'Not Specified' END) as lst_type
FROM crm_mydeals
WHERE status IN (1,2,3)
AND agent_1_id > 0
AND is_active=1
AND DATE(created_date) BETWEEN '2013-11-22' AND '2014-2-22'
GROUP BY status, type, sub_status, created_date