Mysql refrencing derived tables from nested query - mysql

I posted something similar to this yesterday, but now I'd like something a little different from my query-
I'm trying to query a database to retrieve the number of one-time users who have visited a website over time. The data looks something like this:
Day | UserID
1 | A
1 | B
2 | B
3 | A
4 | B
4 | C
5 | D
I'd like the query result to look this this
Time Span | COUNT(DISTINCT UserID)
Day 1 to Day 1 | 2
Day 1 to Day 2 | 1
Day 1 to Day 3 | 0
Day 1 to Day 4 | 1
Day 1 to Day 5 | 2
The result is 2,1,0,1,2 because, at the end of those days, there are X number of users who have visited a single time. e.g. for day 5, at the end of day 5, users c and d have visited only once each.
I think I'm looking for a query similar to this:
select d.day, (select count(distinct userid) from visits where day<=d.day)
from (select distinct day from visits) d
The difference between the query above and what I'm looking for is that I'd like this new query to consider only one-time users for each time span, and not repeat users.
Thanks

This subquery should work for the clarified requirements.
select d.day, count(distinct case when b.userid is null then a.userid end)
from (select day from visits group by day) d
inner join
(
select a.day, a.userid, count(*) c
from visits a
join visits b on a.userid=b.userid and b.day <= a.day
group by a.day, a.userid
having count(*) = 1
) a on a.day <= d.day
left join
(
select a.day, a.userid, count(*) c
from visits a
join visits b on a.userid=b.userid and b.day <= a.day
group by a.day, a.userid
having count(*) > 1
) b on a.userid = b.userid and b.day <= d.day
group by d.day
Original
You must have taken the idea from SQL Server - it is the only RDBMS (IIRC) that will allow you to reference a twice removed (nesting) query. Please indicate what you want and we can rewrite the query.
For the exact query shown, you don't need 2 levels of subquery
SELECT
C.col_c1 AS Data,
(
SELECT count(col_b1)
FROM tbl
WHERE col_b2 <= C.col_c1
) A
FROM (
SELECT col_c1 # subquery to get distinct c1
FROM tbl
GROUP BY col_c1) C;

Related

How to join tables with aggregate functions in MYSQL query?

I have two tables from the database and I want to get the last status of each id. Tried adding the INNER JOIN claus but no avail. These are what my data looks like:
Table: employee
id name department
-------------------------
1 A X
2 B Y
3 C Z
Table: timelog
id time status count
-------------------------------
1 08:51 IN 1
3 09:00 OUT 2
2 09:00 IN 3
2 18:00 OUT 4
1 18:05 OUT 5
Currently, this is the query that I use but need to get the name of each employee.
SELECT
*
FROM timelog
WHERE timelog.count
IN (SELECT MAX(timelog.count)
FROM timelog
GROUP BY timelog.id)
ORDER BY clock.id;
Current output:
id time status
-------------------------------
1 18:05 OUT
2 18:00 OUT
3 09:00 OUT
This is the output I want to achieve:
id name time status
-------------------------------
1 A 18:05 OUT
2 B 18:00 OUT
3 C 09:00 OUT
Is it possible to add JOIN to the above query? If no, what would be the workaround? Any help would be greatly appreciated. TIA.
Use a correlated subquery to get the last record for each id in timelog:
SELECT e.*, tl.*
FROM employee e JOIN
timelog tl
ON e.id = tl.id
WHERE tl.count = (SELECT MAX(tl2.count)
FROM timelog tl2
WHERE tl2.id = tl.id
) ;
Note that your version of the query is not correct. The subquery returns the maximum count for each id. However, the outer query might match a different id to the count in the subquery. The correlation clause fixes this problem.
You can join the tables and use a correlated subquery for filtering:
select
e.id,
e.name,
t.time,
t.status
from employee e
inner join timelog t on t.id = e.id
where t.count = (
select max(count)
from timelog t1
where t1.id = t.id
)

missing data in mysql table

please help me to write this query.I have tried with leftjoin but its not working.
I have two table tdate and tollname. In tdate table I have dates only, like say of one month and second table tollname I have names of toll with dates.
I want to find toll wise dates missing from table tollname.
Table name: tdate
Dates
1
2
3
4
...
30
Tollname
Dates TollName
1 A
1 B
1 C
5 A
5 B
6 C
9 B
12 A
12 B
12 C
28 A
28 B
30 C
You can just use a cross join and left join (or equivalently not exists/not in). This generates all the combinations of the tollname and date, and then returns the ones that are not present in your table:
select d.date, t.tollname
from tdate d cross join
(select distinct tollname from tollname) t
where not exists (select 1
from tollname t2
where d.date = t2.date and t.tollname = t2.tollname
);
If you have a separate table with the tollnames, then you can use that instead of the subquery:
SQL FIDDLE DEMO
SELECT D.*
FROM tdate D
LEFT JOIN Tollname T
ON D.Dates = T.Dates
WHERE T.Dates IS NULL
SELECT d.* from tdate d left join Tollname t on d.Dates = t.Dates
WHERE t.TollName is null

Select four items for each group?

I have got two tables, first one called card:
| card_id | card_name |
And table withdrawal:
| withdrawal_id | card_id | transaction_date |
I want to get the last four transaction_dates for each card_id.
I tried this code but it wouldn't give the last 4 dates:
SELECT a.card_id, b.transaction_date
FROM card AS a
JOIN withdrawal AS b ON a.card_id = b.card_id
GROUP BY a.card_id, b.transaction_date
ORDER BY a.card_id, b.transaction_date
What do I need to change?
It seems like you want to get the first N rows per group, which can be done using something like this:
SELECT a.card_id, b.transaction_date
FROM card a
JOIN withdrawal b ON a.card_id = b.card_id
WHERE(
SELECT COUNT(*)
FROM card c
JOIN withdrawal w ON w.card_id = c.card_id
WHERE c.card_id = a.card_id AND w.transaction_date <= b.transaction_date
) <= 4
ORDER BY a.card_id, b.transaction_date
See this question for more info on getting rows per group.
Here is another way of achieving it
select
w.withdrawal_id,
w.card_id,
w.transaction_date
from card c
left join
(
select
r1.*
from withdrawal r1
where
(
select count(*)
from withdrawal r2
where r1.card_id = r2.card_id
AND r1.transaction_date <= r2.transaction_date
) <=4
order by r1.transaction_date desc
)w
on w.card_id = c.card_id
order by c.card_id
demo

Double join if row in third table matches where clause

I have a game server, and I want to get a list of the most-ignored player accounts.
I have a user table
Table1 - Users:
Name | ID | otherstuff
Troll | 1 | .
CoolGuy | 2 | .
I have an ignore table
Table2 - Ignores
id_UserWhoIsIgnoring | id_UserWhoIsIgnored
2 | 1
3 | 1
Now this is all great, and I can do something like:
select
u.name,
ig.id_UserWhoIsIgnored,
count(ig.id_UserWhoIsIgnored) as ignoreCount
from ignores ig
inner join users u
on ig.id_UserWhoIsIgnored = u.id
group by id_UserWhoIsIgnored
order by ignoreCount desc
limit 25;
But the problem with this is that I get accounts of users who haven't connected in a really long time. I'd like to limit my query to users connected in the past 30 days. I have a third table, sessions
Table3 - Sessions
id_user | start_time | otherstuff
1 | 2014-06-25 00:00:00 | .
(id)OldTroll | 2010-01-01 00:00:00 | .
How can I combine my first query giving the list but restrict it only cases where start_time > date_sub(now(), interval 45 days) gives me a result for id. In this case I don't want a row showing OldTroll even if they're the most ignored because their most recent connection is years old.
If start_time is in the users table, then just use a where:
select u.name, ig.id_UserWhoIsIgnored, count(ig.id_UserWhoIsIgnored) as ignoreCount
from ignores ig inner join
users u
on ig.id_UserWhoIsIgnored = u.id
where start_time > date_sub(now(), interval 45 days)
group by id_UserWhoIsIgnored
order by ignoreCount desc
limit 25;
If start_time is in the ignores table, then just use having:
select u.name, ig.id_UserWhoIsIgnored, count(ig.id_UserWhoIsIgnored) as ignoreCount
from ignores ig inner join
users u
on ig.id_UserWhoIsIgnored = u.id
group by id_UserWhoIsIgnored
having max(start_time) > date_sub(now(), interval 45 days)
order by ignoreCount desc
limit 25;
EDIT:
Then I presume you want:
select u.name, ig.id_UserWhoIsIgnored, count(ig.id_UserWhoIsIgnored) as ignoreCount
from ignores ig inner join
users u
on ig.id_UserWhoIsIgnored = u.id inner join
(select id_user, max(start_time) as start_time
from sessions
group by id_user
) s
on u.id_user = s.id_user and
s.start_time >= date_sub(now(), interval 45 days)
group by id_UserWhoIsIgnored
order by ignoreCount desc
limit 25;

mySQL - GROUP BY but get the most recent row

I've got a budget table:
user_id product_id budget created
-----------------------------------------------------------------
1 1 300 2011-12-01
2 1 400 2011-12-01
1 1 500 2011-12-03
2 2 400 2011-12-04
I've also got a manager_user table, joining a manager with the user
user_id manager_id product_id
------------------------------------
1 5 1
1 9 2
2 5 1
2 5 2
3 5 1
What I'd like to do is grab each of the user that's assigned to Manager #5, and also get their 'budgets'... but only the most recent one.
Right now my statement looks like this:
SELECT * FROM manager_user mu
LEFT JOIN budget b
ON b.user_id = mu.user_id AND b.product_id = mu.product_id
WHERE mu.manager_id = 5
GROUP BY mu.user_id, mu.product_id
ORDER BY b.created DESC;
The problem is it doesn't pull the most recent budget. Any suggestions? Thanks!
To accomplish your task you can do as follows:
select b1.user_id,
b1.budget
from budget b1 inner join (
select b.user_id,
b.product_id,
max(created) lastdate
from budget b
group by b.user_id, b.product_id ) q
on b1.user_id=q.user_id and
b1.product_id=q.product_id and
b1.created=q.lastdate
where b1.user_id in
(select user_id from manager_user where manager_id = 5);
I'm assuming here that your (user_id, product_id, created) combination is unique.
For what it's worth, here's the code that returned what I was looking for:
SELECT DISTINCT(b1.id),mu.user_id,mu.product_id,b1.budget,b1.created
FROM budget b1
INNER JOIN (
SELECT b.user_id, b.product_id, MAX(created) lastdate
FROM budget b
GROUP BY b.user_id, b.product_id) q
ON b1.user_id=q.user_id AND
b1.product_id=q.product_id AND
b1.created=q.lastdate
RIGHT JOIN manager_user mu
ON mu.user_id = b1.user_id AND
mu.product_id = b1.product_id
WHERE mu.manager_id = 5;
Thanks for the help Andrea!