MySQL join tables with subquery - mysql

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

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

MYSQL Subtracting two SELECT Queries

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.

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 get single column value of last record

I have a query which looks like:
SELECT max(sp.id) as max_id, p.name as player, max(update_time) as last_seen, min(login_time) as first_seen, s.name as last_server,
sum(sp.play_time) as ontime_total,
sum(case when login_time > NOW() - INTERVAL 1 DAY then sp.play_time end) as ontime_day,
sum(case when login_time > NOW() - INTERVAL 7 DAY then sp.play_time end) as ontime_week,
sum(case when login_time > NOW() - INTERVAL 1 MONTH then sp.play_time end) as ontime_month
FROM session_player sp
INNER JOIN players p ON p.id=sp.player_id
INNER JOIN server s ON s.id=sp.server_id
WHERE p.name = ?
The result:
The issue:
Node22 isn't the last server. I am struggling on finding a way to get the server of the last record within this query. How would you solve this issue, if possible without running a second query.
(This query already takes 2-3s seconds depending on the user, if possible I would like to avoid any overhead and in case you see performance optimization possibilities I would appreciate anything.)
This would work, but its performance you can guess (4-5s):
SELECT
MAX( sp.id ) AS max_id, p.name AS player, MAX( update_time ) AS last_seen, MIN( login_time ) AS first_seen,
SUM( sp.play_time ) AS ontime_total,
SUM( CASE WHEN login_time > NOW( ) - INTERVAL 1 DAY THEN sp.play_time END ) AS ontime_day,
SUM( CASE WHEN login_time > NOW( ) - INTERVAL 7 DAY THEN sp.play_time END ) AS ontime_week,
SUM( CASE WHEN login_time > NOW( ) - INTERVAL 1 MONTH THEN sp.play_time END ) AS ontime_month,
(SELECT s.name
FROM session_player sp
JOIN players p ON p.id=sp.player_id
JOIN server s ON s.id=sp.server_id
WHERE p.name = ?
ORDER BY sp.id DESC
LIMIT 1
) as last_server
FROM session_player sp
INNER JOIN players p ON p.id = sp.player_id
INNER JOIN server s ON s.id = sp.server_id
WHERE p.name = ?
After nearly 3 hours of experimenting I got it and even 260 times faster as before:
SELECT MAX(pd.id) AS max_id, pd.name AS player, MAX( pd.update_time ) AS last_seen, MIN( pd.login_time ) AS first_seen,
SUM( pd.play_time ) AS ontime_total,
SUM( CASE WHEN pd.login_time > NOW( ) - INTERVAL 1 DAY THEN pd.play_time END ) AS ontime_day,
SUM( CASE WHEN pd.login_time > NOW( ) - INTERVAL 7 DAY THEN pd.play_time END ) AS ontime_week,
SUM( CASE WHEN pd.login_time > NOW( ) - INTERVAL 1 MONTH THEN pd.play_time END ) AS ontime_month,
(SELECT s.name
FROM session_player sp
INNER JOIN server s ON s.id=sp.server_id
WHERE max(pd.id)=sp.id
) as last_server
FROM (
SELECT sp.id AS id, sp.server_id as server_id, p.name AS name, sp.login_time AS login_time, sp.update_time AS update_time, sp.play_time AS play_time
FROM session_player sp
INNER JOIN players p ON p.id=sp.player_id
WHERE p.name = ?
) as pd
Try this:
SELECT sp.id as max_id, p.name as player, max(update_time) as last_seen,
min(login_time) as first_seen, s.name as last_server,
sum(sp.play_time) as ontime_total,
sum(case when login_time > NOW() - INTERVAL 1 DAY then sp.play_time end) as ontime_day,
sum(case when login_time > NOW() - INTERVAL 7 DAY then sp.play_time end) as ontime_week,
sum(case when login_time > NOW() - INTERVAL 1 MONTH then sp.play_time end) as ontime_month
FROM session_player sp
INNER JOIN players p ON p.id=sp.player_id
INNER JOIN server s ON s.id=sp.server_id
WHERE p.name = ?
group by sp.player_id
order by sp.id desc limit 1

mysql union all with aliases, syntax error

Why do I get
Error in query (1064): Syntax error near 'as q2)' at line 7
with
SELECT SQL_NO_CACHE q1.d1, q1.a, q2.b, (q1.a-q2.b)/q1.a*100 as Percentage
FROM
(SELECT Date(date) d1, count(id_update) a
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
GROUP BY DATE(date)) as q1
UNION ALL
(SELECT date(date) as d2, count(id_update) as b
FROM vas_updates
WHERE date BETWEEN
date_sub(date_sub(now(), interval 1 day), interval 2 hour)
AND
date_sub(now(), interval 1 day) group by DATE(d2) ) as q2
Can't I use aliases with UNION?
UPDATE:
this query might have leftovers from another query, I was tyring to understand the syntax error first.
What I'm trying to calculate is the percentage increase or decrease of two sums which are the hits from the last 2 hours of today compared to same timeframe from yesterday.
the table has just id and datetime
I suspect you actually want a JOIN
Something like this:-
SELECT SQL_NO_CACHE q1.d1, q1.a, q2.b, (q1.a-q2.b)/q1.a*100 as Percentage
FROM
(
SELECT Date(date) d1, count(id_update) a
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
GROUP BY DATE(date)
) as q1
INNER JOIN
(
SELECT date(date) as d2, count(id_update) as b
FROM vas_updates
WHERE date BETWEEN date_sub(date_sub(now(), interval 1 day), interval 2 hour) AND date_sub(now(), interval 1 day)
group by DATE(d2)
) as q2
ON q1.d1 = q2.d2
EDIT
Checked your updated query and it IS a JOIN you need.
You can use a CROSS JOIN. You are returning 1 value from each sub query, and doing a calculation on those values:-
SELECT SQL_NO_CACHE q1.d1, q1.a, q2.b, (q1.a-q2.b)/q1.a*100 as Percentage
FROM
(
SELECT MIN(Date(date)) d1, count(id_update) a
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
) as q1
CROSS JOIN
(
SELECT MIN(Date(date)) d2, count(id_update) as b
FROM vas_updates
WHERE date BETWEEN
date_sub(date_sub(now(), interval 1 day), interval 2 hour)
AND
date_sub(now(), interval 1 day)
) as q2
CROSS JOIN gives you every combination of the rows. In this case you have 1 resulting record. I have just returned the MIN date to get a single date to display.
You Can't. A UNION operation does not allow you to use alias on subqueries as it is an operation that creates a single table.
Like this:
select 1 a, 2 b
union all
select 3 blah, 4 bleh
This will result in
a b
1 2
3 4
See it here: http://sqlfiddle.com/#!2/68b32/444
On this query you only have two fields no matters what is on the second query it will only parse the first one, check if the others querys has the same quantity of fields as the first and if they are of the same type. Name the UNIONed querys with alies is invalid.
So I think what you need is probably a JOIN OR just all the fields
So, your query would be something like:
SELECT SQL_NO_CACHE tbl.d1,
tbl.a,
tbl.b,
(tbl.a-tbl.b)/tbl.a*100 as Percentage
FROM (SELECT Date(date) d1,
count(id_update) a,
null d2,
null b
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
GROUP BY DATE(date)
UNION ALL
SELECT null d1,
null a
date(date) as d2,
count(id_update) as b
FROM vas_updates
WHERE date
BETWEEN date_sub(date_sub(now(), interval 1 day), interval 2 hour)
AND date_sub(now(), interval 1 day) group by DATE(d2)
) tbl
But this most likely will not make the calculations right. You can use the version that #Kickstart has provided you.
Query of the answer from #Kickstart
SELECT SQL_NO_CACHE q1.d1, q1.a, q2.b, (q1.a-q2.b)/q1.a*100 as Percentage
FROM
(
SELECT Date(date) d1, count(id_update) a
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
GROUP BY DATE(date)
) as q1
INNER JOIN
(
SELECT date(date) as d2, count(id_update) as b
FROM vas_updates
WHERE date BETWEEN date_sub(date_sub(now(), interval 1 day), interval 2 hour) AND date_sub(now(), interval 1 day)
group by DATE(d2)
) as q2
ON q1.d1 = q2.d2
I decided to put this answer to explain why you are using the UNION operation in a wrong way.
I think that #Kickstart is right,and you can try this.
SELECT SQL_NO_CACHE d, a
FROM
(SELECT Date(date) d, count(id_update) as a
FROM vas_updates
WHERE date > date_senter code hereub(now(), interval 2 hour)
GROUP BY DATE(date))
UNION ALL
(SELECT date(date) as d, count(id_update) as a
FROM vas_updates
WHERE date BETWEEN
date_sub(date_sub(now(), interval 1 day), interval 2 hour)
AND
date_sub(now(), interval 1 day) group by DATE(d2) )
I'm wrong,UPDATE, you can try like this
SELECT SQL_NO_CACHE q1.d1, q1.a, q2.b, (q1.a-q2.b)/q1.a*100 as Percentage
FROM
(
SELECT DATE_FORMAT(Date(date),'%H') as d1, count(id_update) a
FROM vas_updates
WHERE date > date_sub(now(), interval 2 hour)
GROUP BY DATE(date)
) as q1
INNER JOIN
(
SELECT DATE_FORMAT(Date(date),'%H') as d2, count(id_update) as b
FROM vas_updates
WHERE date BETWEEN date_sub(date_sub(now(), interval 1 day), interval 2 hour) AND date_sub(now(), interval 1 day)
group by DATE(d2)
) as q2
ON q1.d1 = q2.d2