MYSQL Subtracting two SELECT Queries - mysql

Using MYSQL, I have written two big SELECT queries combined by a UNION, to get 2 rows, where the first row is the count for the current month, and the second row is the count for the previous month. The Query is as follows:
select * from
(select count(*) as type1 from table_x where nationality_id = 23 and month(START_DATE) = month(now())) as t1,
(select count(*) as type2 from table_x where nationality_id = 24 and month(START_DATE) = month(now())) as t2,
(select count(*) as type3 from table_x where nationality_id = 25 and month(START_DATE) = month(now())) as t3,
(select count(*) as type4 from table_x where nationality_id = 26 and month(START_DATE) = month(now())) as t4
UNION
select * from
(select count(*) as type1 from table_x where nationality_id = 23 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t1,
(select count(*) as type2 from table_x where nationality_id = 24 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t2,
(select count(*) as type3 from table_x where nationality_id = 25 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t3,
(select count(*) as type4 from table_x where nationality_id = 26 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t4
I want to add a third row, which is the difference between row 2 and row 1.
How can I do this with my current query?

You are obviously doing a compare between current and prior month. So, I would start with my inner pre-query aggregate getting only those transactions >= the first of the prior month AND the records within the nationality IDs you are looking for.
The inner date_sub() of DAYOFMONTH() -1 day gives you the first of the CURRENT month. By subtracting one more month, gives you the first of the LAST month.
Now you can aggregate the totals per nationality compared to current month or not. That inner query gives you all begin and end counts. Now that is wrapped to the outer and you can get all the counts in addition to the differences... Obviously you can change the column names respectively.
select
PQ.*,
PQ.ThisMonth23 - PQ.LastMonth23 = Diff23,
PQ.ThisMonth24 - PQ.LastMonth24 = Diff24,
PQ.ThisMonth25 - PQ.LastMonth25 = Diff25,
PQ.ThisMonth26 - PQ.LastMonth26 = Diff26
from
( select
sum( case when t.Nationality_id = 23 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth23,
sum( case when t.Nationality_id = 24 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth24,
sum( case when t.Nationality_id = 25 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth25,
sum( case when t.Nationality_id = 26 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth26,
sum( case when t.Nationality_id = 23 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth23,
sum( case when t.Nationality_id = 24 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth24,
sum( case when t.Nationality_id = 25 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth25,
sum( case when t.Nationality_id = 26 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth26
from
table_x t
where
t.StartDate >= date_sub( date_sub( t.StartDate, interval DAYOFMONTH( t.StartDate ) -1 DAY ), interval 1 MONTH )
AND t.Nationality_id IN ( 23, 24, 25, 26 )
) PQ
I would just add that your query might be getting more than you think... You are asking for ALL records Ex: January REGARDLESS of the year, compared to ALL records December REGARDLESS of the year because all you are qualifying is based on the MONTH() and no YEAR() consideration. I am explicitly querying back only current and prior month.

Related

QOQ growth Mysql

i have query
select a.`2021`,
b.`2022`,
a.product,
concat(ceil((b.`2022`-a.`2021`)/ a.`2021` * 100), '%') as growth
from ( SELECT SUM(total) as `2021`,
product,
sum
FROM table
WHERE YEAR(month) = 2021
AND case when day(CURRENT_DATE()) > 10
then QUARTER(month) = QUARTER(CURRENT_DATE() - INTERVAL 1 MONTH)
else QUARTER(month) = QUARTER(CURRENT_DATE() - INTERVAL 3 MONTH)
end
GROUP BY Product ,
YEAR(month) )a
JOIN ( SELECT SUM(total) as `2022`,
Product
FROM table
WHERE YEAR(month) = 2022
AND case when day(CURRENT_DATE()) > 10
then QUARTER(month) = QUARTER(CURRENT_DATE() - INTERVAL 1 MONTH)
else QUARTER(month) = QUARTER(CURRENT_DATE() - INTERVAL 3 MONTH)
end
GROUP BY Product ,
YEAR(month) ) b on a.Product=b.Product;
if the current date is not the end of the quarter then the data that appears is the data in the previous quarter period

run multiple mysql queries as 1 query

im trying to take 3 sql queries and insert them into 1 table without getting the null value's and using a group by number as to not get duplicate numbers in the same column.
I have the issue where running query 1 leaves me with a bunch of null data values
and running query 2 doesnt group the numbers resulting in thousands of rows numbers only go up to 100
QUERY 1
insert into table ( number)
select number as 1day from table where date = CURDATE() - interval 1day group by number
insert into table ( number)
select number as 2day from table where date = CURDATE() - interval 1day group by number
insert into table ( number)
select number as 7day from table where date = CURDATE() - interval 1day group by number
so i try to run
QUERY 2
insert into table (number,number,number)
select
*
from
(select number as 1day from test.test where date = curdate() - interval 1 day group by
number) as 1day,
(select number as 2day from test.test where date > curdate() - interval 2 day group by
number) as 2day,
(select number as 7day from test.test where date > curdate() - interval 7 day group
by number) as 7day;
try the below:
insert into table (number,number,number)
select
table.1day,table.2day,table.7day
from
((select number from test.test where date = curdate() - interval 1 day group by
number) as 1day,
(select numberfrom test.test where date > curdate() - interval 2 day group by
number) as 2day,
(select number from test.test where date > curdate() - interval 7 day group
by number) as 7day) as table
select (case one.number when two.number then null else one.number end) as '1day',(case two.number <= third.number when true then (case one.number = two.number when true then null else two.number end) else (case one.number = two.number when false then null else two.number end) end) as '2day',(case (third.number < one.number and third.number = two.number) when true then null else third.number end) as '7day'
from (
(select x.number
from (
(select number,'1day' as 'type' from testtable where date = curdate() - interval 1 day group by number)
union all
(select number,'2day' as 'type' from testtable where date > curdate() - interval 2 day group by number)
union all
(select number,'7day' as 'type' from testtable where date > curdate() - interval 7 day group by number)) as x
where x.type='2day' order by x.number) as two,
(select x.number
from (
(select number,'1day' as 'type' from testtable where date = curdate() - interval 1 day group by number)
union all
(select number,'2day' as 'type' from testtable where date > curdate() - interval 2 day group by number)
union all
(select number,'7day' as 'type' from testtable where date > curdate() - interval 7 day group by number)) as x
where x.type='1day' order by x.number) as one,
(select x.number
from (
(select number,'1day' as 'type' from testtable where date = curdate() - interval 1 day group by number)
union all
(select number,'2day' as 'type' from testtable where date > curdate() - interval 2 day group by number)
union all
(select number,'7day' as 'type' from testtable where date > curdate() - interval 7 day group by number)) as x
where x.type='7day' order by x.number) as third
)
where ((one.number = two.number) or (one.number is null or two.number is null)) or
((third.number = two.number) or (two.number is null or third.number is null))

How to limit query results by a percentage of the total number of results in MySQL?

The below mysql question returns only the 10 first rows. How can I limit the them to 10% of all?
SELECT page,
poso,
diff
FROM (SELECT page,
Count(*) AS poso,
( Sum(Date(timestamp) = Curdate()) - Sum(
Date(timestamp) = Date_sub(Curdate(),
INTERVAL 1 day)) )
diff
FROM `behaviour`
WHERE Date(timestamp) >= Date_sub(Curdate(), INTERVAL 1 day)
GROUP BY page
ORDER BY ( Sum(Date(timestamp) = Curdate()) - Sum(
Date(timestamp) = Date_sub(Curdate(),
INTERVAL 1 day))
) DESC
LIMIT 10) AS u
ORDER BY diff DESC
Adapted from the answer to the duplicate question:
SELECT page,
poso,
diff
FROM (
SELECT *,
#counter := #counter + 1 AS counter
FROM (select #counter:=0) AS initvar,
(SELECT page,
Count(*) AS poso,
( Sum(Date(timestamp) = Curdate()) - Sum(
Date(timestamp) = Date_sub(Curdate(),
INTERVAL 1 day)) )
diff
FROM `behaviour`
WHERE Date(timestamp) >= Date_sub(Curdate(), INTERVAL 1 day)
GROUP BY page
ORDER BY ( Sum(Date(timestamp) = Curdate()) - Sum(
Date(timestamp) = Date_sub(Curdate(),
INTERVAL 1 day))
) DESC) AS u
) AS v
WHERE counter <= 10/100 * #counter
ORDER BY diff DESC;
Demo here: http://rextester.com/JKMBZR62923

MySQL join tables with subquery

I have this crazy query below. I want to organize them but don't know how.
Can someone help me run this query? I want to join the 5 sub-query tables below and at the end, the result would be a table displaying line item_id with last5days, last10days, last30days, last60days, last90days.
Thanks a bunch!
Select q1.line_item_id,
q1.domains as 'last5days',
q2.domains as 'last10days',
q3.domains as 'last30days',
q4.dpmains as 'last60days',
q5.domains as 'last90days'
From q1
Join q2 on q1.line_item_id = q2.line_item_id
Join q3 on q1.line_item_id = q3.line_item_id
Join q4 on q1.line_item_id = q4.line_item_id
Join q4 on q1.line_item_id = q5.line_item_id
select q1.line_item_id, count(*) domains
from (
select distinct line_item_id, domain
from rpt_domain_by_campaign
where event_date between DATE_SUB(curdate(), INTERVAL 5 DAY)
and now()
) q1
group by q1.line_item_id
select q1.line_item_id, count(*) domains
from (
select distinct line_item_id, domain
from rpt_domain_by_campaign
where event_date between DATE_SUB(curdate(), INTERVAL 10 DAY)
and now()
) q2
group by q1.line_item_id
select q1.line_item_id, count(*) domains
from (
select distinct line_item_id, domain
from rpt_domain_by_campaign
where event_date between DATE_SUB(curdate(), INTERVAL 30 DAY)
and now()
) q3
group by q1.line_item_id
select q1.line_item_id, count(*) domains
from (
select distinct line_item_id, domain
from rpt_domain_by_campaign
where event_date between DATE_SUB(curdate(), INTERVAL 60 DAY)
and now()
) q4
group by q1.line_item_id
select q1.line_item_id, count(*) domains
from (
select distinct line_item_id, domain
from rpt_domain_by_campaign
where event_date between DATE_SUB(curdate(), INTERVAL 90 DAY)
and now()
) q5
group by q1.line_item_id
Just use sum and case make more easy:
select line_item_id,
Sum( CASE WHEN DATEDIFF(curdate(), event_date) <= 5 then 1 ELSE 0 END ) 'last5days',
Sum( CASE WHEN DATEDIFF(curdate(), event_date) <= 10 then 1 ELSE 0 END ) 'last10days',
Sum( CASE WHEN DATEDIFF(curdate(), event_date) <= 30 then 1 ELSE 0 END ) 'last30days',
Sum( CASE WHEN DATEDIFF(curdate(), event_date) <= 60 then 1 ELSE 0 END ) 'last60days',
Sum( CASE WHEN DATEDIFF(curdate(), event_date) <= 90 then 1 ELSE 0 END ) 'last90days'
from rpt_domain_by_campaign
Group by line_item_id
I recommend using count(distinct...) to avoid the extra layer of nesting in each of your sub-queries.
I also recommend switching the order of sub-queries and using outer joins because not every line item with rows in the past 90 days will also have rows in the past 60 days, etc.
Try something like this:
Select q1.line_item_id,
q1.domains as last90days,
coalesce(q2.domains,0) as last60days,
coalesce(q3.domains,0) as last30days,
coalesce(q4.domains,0) as last10days,
coalesce(q5.domains,0) as last5days
from
(
select line_item_id, count(distinct domain) as domains
from rpt_domain_by_campaign
where event_date between DATE_SUB(curdate(), INTERVAL 90 DAY)
and now()
group by line_item_id
) q1
left outer join
(
select line_item_id, count(distinct domain) as domains
from rpt_domain_by_campaign
where event_date between DATE_SUB(curdate(), INTERVAL 60 DAY)
and now()
group by line_item_id
) q2 on q1.line_item_id = q2.line_item_id
left outer join
(
select line_item_id, count(distinct domain) as domains
from rpt_domain_by_campaign
where event_date between DATE_SUB(curdate(), INTERVAL 30 DAY)
and now()
group by line_item_id
) q3 on q1.line_item_id = q3.line_item_id
left outer join
(
select line_item_id, count(distinct domain) as domains
from rpt_domain_by_campaign
where event_date between DATE_SUB(curdate(), INTERVAL 10 DAY)
and now()
group by line_item_id
) q4 on q1.line_item_id = q4.line_item_id
left outer join
(
select line_item_id, count(distinct domain) as domains
from rpt_domain_by_campaign
where event_date between DATE_SUB(curdate(), INTERVAL 5 DAY)
and now()
group by line_item_id
) q5 on q1.line_item_id = q5.line_item_id
Just use group by max date
select line_item_id,
Sum( CASE WHEN DATEDIFF(curdate(), event_date) <= 5 then 1 ELSE 0 END ) 'last5days',
Sum( CASE WHEN DATEDIFF(curdate(), event_date) <= 10 then 1 ELSE 0 END ) 'last10days',
Sum( CASE WHEN DATEDIFF(curdate(), event_date) <= 30 then 1 ELSE 0 END ) 'last30days',
Sum( CASE WHEN DATEDIFF(curdate(), event_date) <= 60 then 1 ELSE 0 END ) 'last60days',
Sum( CASE WHEN DATEDIFF(curdate(), event_date) <= 90 then 1 ELSE 0 END ) 'last90days'
from
(
select line_item_id, max(event_date) event_date
from
rpt_domain_by_campaign
where event_date < curdate()
group by line_item_id, domain
) a
Group by line_item_id

How to select 6 months of data in MySQL, grouped between the 22nd and 21st of the next month

I have a table that contains the amount of data used each day, it looks something like this:
date | bytes
------------------
2014-01-1 | 12345
2014-01-2 | 56789
2014-01-3 | 78901
...
2014-02-1 | 12345
2014-02-2 | 56789
2014-02-3 | 78901
...
What I need to do is get the last 6 monthly totals, however the month must start on the 22nd day of the month and finish on the 21st day of the following month. For the current month it should start on the 22nd and finish today.
The best I can come up with is the following, the problem is - it is very messy and doesn't seem to give the correct result.
SELECT monthname(`date`),sum(`bytes`)
FROM `trafficDaily`
WHERE `date` between STR_TO_DATE( CONCAT( "22,", MONTH( NOW( ) )-6 , ",", YEAR( NOW( ) ) ) , "%d,%m,%Y" )
and STR_TO_DATE( CONCAT( "21,", MONTH( NOW( ) ) , ",", YEAR( NOW( ) ) ) , "%d,%m,%Y" )
group by month(DATE_SUB(`date`, INTERVAL 21 DAY))
order by `date`
Thank you in advance for your help.
You can do so by using user-defined variables to track the the change of month i.e in your case month starts from 21st
SELECT
MONTHNAME(STR_TO_DATE(group_day, '%m')) month_name ,
SUM(`bytes`) `sum`
FROM (
SELECT *,
#changemonth:= CASE
WHEN DAY(`date`) > 21
THEN #month
WHEN MONTH(`date`) <> #month
THEN #month
ELSE #month - 1
END group_day,
#month:= MONTH(`date`)
FROM
t ,(SELECT #changemonth:=0,
#month:= (SELECT MONTH(`date`) FROM t
WHERE `date` > NOW() - INTERVAL 6 MONTH ORDER BY `date` LIMIT 1) aa
) tt
WHERE `date` > NOW() - INTERVAL 6 MONTH
ORDER BY `date`
) a
GROUP BY group_day
Demo for last 3 months
Edit from comments for the case when January lies in last 6 month period
SELECT
MONTHNAME(
STR_TO_DATE(
CASE WHEN group_day < 1
THEN 12 ELSE group_day
END, '%m'
)
) month_name ,
SUM(`bytes`) `sum`
FROM (
SELECT *,
#changemonth:= CASE
WHEN DAY(`date`) > 21
THEN #month
WHEN MONTH(`date`) <> #month
THEN #month
ELSE #month - 1
END group_day,
#month:= MONTH(`date`)
FROM
t ,(SELECT #changemonth:=0,
#month:= (SELECT MONTH(`date`) FROM t
WHERE `date` > NOW() - INTERVAL 6 MONTH ORDER BY `date` LIMIT 1) aa
) tt
WHERE `date` > NOW() - INTERVAL 6 MONTH
ORDER BY `date`
) a
GROUP BY group_day
Demo with January
You have at least two options:
1st option
Create a calendar table and assign the 'business month' to each days. You can prepare your table for a long time period, then you can join to that table by date and you can do the grouping. If you have to do this calculation regularry, this is a good solution. (You can upgrade and use the calendar table to do several tasks)
2nd option
You can calculate the 'business month' by the date using the following query. (Please note, that I did not tested this query, so there could be typos).
SELECT
CASE
WHEN DAY(date) >= 22 THEN CONCAT(YEAR(date), '-', MONTH(date))
ELSE CONCAT(YEAR(date - INTERVAL 1 MONTH), '-', MONTH(date - INTERVAL 1 MONTH))
END AS m,
SUM(bytes)
FROM
log -- Use your table name instead :)
GROUP BY
CASE
WHEN DAY(date) >= 22 THEN CONCAT(YEAR(date), '-', MONTH(date))
ELSE CONCAT(YEAR(date - INTERVAL 1 MONTH), '-', MONTH(date - INTERVAL 1 MONTH))
END
You can adjust the calculation to your needs.