How to connect 2 queries SQL - mysql

How to connect these 2 queries to receive one that gives such a response:
user_name room_name_reserved room_name_biggest_time
user_1 room_1 room_2
room_name_reserved - most often reserved room by user
room_name_biggest_time - room when user spent the most time
1 query:
SELECT
us.name as user_name
,ro.name as room_name_reserved
FROM reservation re
INNER JOIN user us on us.id = re.userid
INNER JOIN room ro on ro.id = re.roomid
GROUP BY us.name, ro.name
HAVING COUNT(room_name_reser) = (SELECT COUNT(*) FROM reservation re1
WHERE re1.userid = re.userid
GROUP BY re1.roomid
ORDER BY COUNT(*) DESC
LIMIT 1
)
2 query :
SELECT
us.name as user_name
,ro.name as room_name_biggest_time
from Reservation re
INNER JOIN user us on us.id = re.userid
INNER JOIN room ro on ro.id = re.roomid
WHERE (strftime('%s', re.EndsAt) - strftime('%s', re.StartsAt)) = (SELECT MAX((strftime('%s', re1.EndsAt) - strftime('%s', re1.StartsAt))) as time FROM Reservation re1 where re1.UserId = re.userid
ORDER BY time DESC
LIMIT 1 )

Use the two query as subquery in join
select t1.user_name, t1.room_name_reserved , t2.room_name_biggest_time
from (
select
us.name as user_name,
ro.name as room_name_reserved
from reservation re
inner join user us on us.id = re.userid
inner join room ro on ro.id = re.roomid
group by us.name, ro.name
having count(room_name_reser) = (
select count(*)
from reservation re1
where re1.userid = re.userid
group by re1.roomid
order by count(*) desc
limit 1)
) t1
left join (
select
us.name as user_name,
ro.name as room_name_biggest_time
from Reservation re
inner join user us on us.id = re.userid
inner join room ro on ro.id = re.roomid
Where (strftime('%s', re.EndsAt) - strftime('%s', re.StartsAt)) = (
SELECT Max((strftime('%s', re1.EndsAt) - strftime('%s', re1.StartsAt))) as time
from Reservation re1
where re1.UserId = re.userid
order by time desc limit 1
)
) t2 on t1.user_name = t2.user_name

Assuming you are using MySql, here's a little hack that can work:
SELECT
user_name,
SUBSTRING_INDEX(group_concat(room_name ORDER BY n_reservations DESC), ',', 1) as room_name_reserved,
SUBSTRING_INDEX(group_concat(room_name ORDER BY total_stay DESC), ',', 1) as room_name_biggest_time
FROM (
SELECT
us.name as user_name,
ro.name as room_name,
count(*) as n_reservations,
sum(strftime('%s', re.EndsAt) - strftime('%s', re.StartsAt)) as total_stay
FROM user u
JOIN reservation re on us.id = re.userid
JOIN room ro on ro.id = re.roomid
GROUP BY us.name, ro.name
) t
GROUP BY user_name
Explanation:
With the subquery, aliased as t, we extract the total number of reservations (count(*)) and the total staying time (sum(...)) for each user/room, producing an intermediate output as follows:
user room n_reservations total_stay
--------------------------------------
user1 room1 1 6000
user1 room2 2 3000
user1 room3 1 4000
user2 room2 1 9000
user2 room4 5 4000
With the outer query we GROUP BY user, and for each user we first group concat the room name using a sorting based on counts and sums made before (biggest value first), producing an intermediate output as follow:
user group_concat#1 group_concat#2
---------------------------------------
user1 room2,room1,room3 room1,room3,room2
user2 room4,room2 room2,room4
Last step, we split by comma the output of the two group_concat mantaining only the first value, so final result will be:
user room_name_reserved room_name_biggest_time
-----------------------------------------------
user1 room2 room1
user2 room4 room2

Related

I need to get specific ids from db if these are in current and last quarter using SQL

[DB Table]
SELECT b.first_name, b.last_name, a.pod_name, a.category, c.user_id,
SUM(IF(QUARTER(CURDATE())-1 OR (QUARTER(CURDATE())-2) AND a.user_id, 1, 0)) AS flag FROM kudos a
INNER JOIN users b ON a.user_id = b.id INNER JOIN users_groups c ON a.user_id = c.user_id
INNER JOIN groups d ON c.group_id = d.id WHERE a.group_name = 'G2' AND d.id IN (7,8,9,11,12,13,14,15,16,17,21,22,23,24,25,26,27,28)
AND QUARTER(CURDATE())-1 = a.quarter ORDER BY a.final_score+0 DESC
I need to get the user_ids of those users which are both in quarter 1 and 2 from table.
Tried above query but failed to get expected results.
Can someone please guide me on this?
if you only need user_id then you can do this :
select user_id
from tablename
where quarter in (1,2)
group by user_id
having count(distinct quarter) = 2
another way is to use window function, assuming you have one user id in each quarter:
select * from (
select * , count(*) over (partition by user_id) cn
from tablename
where quarter in (1,2)
) t where cn = 2

Adding a extra subquery to MySQL query

So i have 2 tables, users and messages
users table
uid, username
messages table
mid, senderid, receiverid, message, timestamp
The query i have at the moment is retrieving the uid, username and last timestamp of each chat conversation
SELECT DISTINCT
IF (messages.senderid = '5e9b95786a71f8.25790415', messages.receiverid, messages.senderid) as uid,
(SELECT username FROM users WHERE uid = IF (messages.senderid = '5e9b95786a71f8.25790415', messages.receiverid, messages.senderid)) as username,
MAX(messages.timestamp) as last_timestamp
FROM messages JOIN users ON users.uid = messages.senderid WHERE messages.senderid = '5e9b95786a71f8.25790415' OR messages.receiverid = '5e9b95786a71f8.25790415'
GROUP BY 1,2
which outputs below
uid | username | last_timestamp
1 | Developer | 1601826378
2 | BetaTester | 1601826301
What i need to add to this query is the message field which is based on the last_timestamp i have in the output.
How can the query be updated to include the extra message field?
Try this.
select A.uid, C.username, B.timestamp, B.message from (SELECT DISTINCT
IF (messages.senderid = '5e9b95786a71f8.25790415', messages.receiverid, messages.senderid) as uid,
MAX(messages.mid) as max_mid
FROM messages WHERE messages.senderid = '5e9b95786a71f8.25790415' OR messages.receiverid = '5e9b95786a71f8.25790415'
GROUP BY 1) A join messages B on A.max_mid = B.mid join users C on A.uid = C.uid
I'm a little confuse, since you already got last_timestamp why don't just left join message table again.
left JOIN messages m2 on last_timestamp = m2.timestamp and mid = m2.mid
and just select message column
select m2.message

How to find the most common value after joining three tables

I have to write a SQL query that, for every user, will return the name of the room that was the most frequently reserved by the user.
I created one of the three tables:
SELECT User.Name as user_name, Room.Name as room_reser
FROM Reservation
INNER JOIN User ON User.Id = Reservation.UserId
INNER JOIN Room ON Room.Id = Reservation.RoomId
Table of 3:
Name room_rese name common_room
Jack room_1
Anna room_2 I need => Jack room_1
Jack room_1
Anna room_1 Anna room_2
Jack room_2
Anna room_2
I tried something like this but I don't know how to use it in this case :
SELECT DISTINCT r.user_name, (
select b.room_reser
from Reservation b
where b.user_name = r.user_name
group by b.user_name, b.roo_reser
order by count(*) desc
limit 1
) as roo_reser from Reservation r)`
If you are running a database that supports window functions, you can do this with aggregation and window function rank():
select user_name, room_name
from (
select
us.name as user_name,
ro.name as room_name,
rank() over(partition by re.userid order by count(*) desc) rn
from reservation re
inner join user us on us.id = re.userid
inner join room ro on ro.id = re.roomid
group by re.userid, re.roomid, us.name, ro.name
) t
where rn = 1
The inner query aggregates by user name and room, and ranks rooms per user. The outer query filters on the top room per user. If there are ties (ie the two most reserved room of a user have the same number of reservations), then both will be displayed - if you want a single record even if there are ties, you can add another sorting criteria to break the tie.
If your database does not support window function, you could try and filter in the having clause with an aggregate correlated subquery:
select
us.name as user_name,
ro.name as room_name
from reservation re
inner join user us on us.id = re.userid
inner join room ro on ro.id = re.roomid
group by us.name, ro.name
having count(*) = (
select count(*)
from reservation re1
where re1.userid = re.userid
group by re1.roomid
order by count(*) desc
limit 1
)
SELECT DISTINCT User.Name as user_name, Room.Name as room_reser
FROM Reservation
INNER JOIN User ON User.Id = Reservation.UserId
INNER JOIN Room ON Room.Id = Reservation.RoomId
GROUP BY user_name, room_reser
ORDER BY COUNT(room_reser)

MySQL query to get last 4 records of a related table

I have a database with two tables: users and payments.
Each user has many payments and each payment can be successful or failed.
I need to write a query to get all the users who failed the last 4 payments.
This is what I tried so far:
select *
from users u
where u.id in(
select p.user_id
from payments
where p.status = 'failed'
group by p.user_id
having count(p.id) = 4
);
But as you can see this is not only checking for the last 4 payments, but all of them. So, it is returning the users that have failed 4 payments (in global, not only the last 4).
I don't know if it is important but the fields on the tables are:
users:
id | name | email | password
payment:
id | date | status | user_id
| | (can be success or failed) | (FK)
Update:
This sqlfiddle will help to understand what I need.
The query is returning all users with 4 failed payments. But I only need the users whose 4 most recent payments failed. In this case it will be only user with id 5
This works
SELECT x.user_id, count(*) as cnt
FROM (
SELECT a.user_id, a.date, a.status FROM payment AS a WHERE
(SELECT COUNT(*) FROM payment AS b
WHERE b.user_id = a.user_id AND b.date >= a.date) <= 4
ORDER BY a.user_id ASC, a.date DESC) AS x
WHERE x.status = 'failed'
GROUP BY x.user_id
HAVING cnt >=4;
If you want the users, whose last 4 transactions were failed (only last 4, not total 4) then following query should get the job done:
select u.* from users u
where
id in
(select p.user_id from payment p
where (select count(*) from payment p1
where p.user_id = p1.user_id
and p.date <= p1.date
order by p1.user_id asc,p1.date desc
) <= 4
and p.status <> 'success'
group by p.user_id
having count(*)>=4);
check the sqlfiddle
Hope it helps!
You want to use the LIMIT keyword, and specify an ORDER.
Try this
select *
from users u
where u.id in(
select p.user_id
from payments
where p.status = 'failed'
group by p.user_id
having count(p.id) = 4
) ORDER BY p.id DESC LIMIT 4;
Not entirely sure what you are trying to do inside the WHERE statement, but ORDER BY p.id DESC LIMIT 4 will retrieve the four most recent rows.
I think you can use a query like this:
select users.id, users.name, users.email, users.password
from users
left join (
select p1.id, p1.date, p1.status, p1.user_id,
count(p2.id) seq -- this count() creates a sequence number for each user ordered by date
from payment p1
left join payment p2
on p1.user_id = p2.user_id -- here I set sequence for each user
and p1.date <= p2.date -- here I set sequence is ordered by data
group by p1.id, p1.date, p1.status, p1.user_id
) t
on users.id = t.user_id
where t.seq < 5 -- Now filter last 4 sequences of each user's payments
and t.status = 'failed'
group by users.id, users.name, users.email, users.password
having count(*) = 4; -- At last filter those have 4 failed in last 4 sequences
[ SQL Fiddle Demo ]

Followers of a user and ids of last 2 comments liked by each follower(if any), also the count of follower's followers, using mysql

I have social application let's say like twitter, where user can follow other users and can likes some comments.
What I need to fetch is followers of a user and ids of last 2 comments liked by each follower(if any), also the count of follower's followers, using MySQL.
Here are the tables
Table user_follower
User_id follower_id
1 2
2 3
1 5
1 6
1 7
Table user_likes
comment_id User_id date
41 2 some_date
42 2 some_date
41 5 some_date
42 5 some_date
43 5 some_date
43 2 some_date
43 6 some_date
how can we do this in a single mysql query?
so far i am able to get the followers and count of follower's follower and following both.
select uf.follower_id,
(select count(*) from user_followers uf1 where uf1.follower_id = uf.follower_id) as following_count,
(select count(*) from user_followers uf2 where uf2.user_id = uf.follower_id) as follower_count,
from user_followers uf
join users u on u.id = uf.follower_id
where uf.user_id = 1
what i want is now to get the 2 latest comment_ids for each follower, i.e. uf.follower_id here.
if not possible in the same query,
i am fine even with another query as will passing the follower_ids as in parameter, but it should give me 2 latest comment for each passed id..
I think this will work. I have not tested it so it may have some syntax errors. Given the level of nesting used in this query I suspect it will perform badly with a very large dataset.
SELECT follower_id, num_followers, GROUP_CONCAT(comment_id)
FROM (
SELECT t.*,
#r := IF(#g = t.follower_id, #r+1, 1) RowNum,
#g := t.follower_id
FROM (select #g:=null) AS initvars
INNER JOIN (
SELECT followers.*, ul.comment_id
FROM (
SELECT
uf1.user_id,
uf1.follower_id,
COUNT(uf2.follower_id) AS num_followers
FROM user_follower uf1
LEFT JOIN user_follower uf2
ON uf1.follower_id = uf2.user_id
WHERE uf1.user_id = 1
GROUP BY uf1.user_id, uf1.follower_id
) AS followers
LEFT JOIN user_likes ul
ON followers.follower_id = ul.user_id
ORDER BY followers.follower_id ASC, comment_id DESC
) AS t
) AS final
WHERE RowNum < 3
GROUP BY follower_id, num_followers;
UPDATE Here is the other query using the inequality join -
SELECT tmp.follower_id, COUNT(uf2.follower_id) AS num_followers, tmp.comments
FROM (
SELECT follower_id, GROUP_CONCAT(comment_id ORDER BY comment_id DESC) AS comments
FROM (
SELECT uf.follower_id, ul1.*
FROM user_follower uf
LEFT JOIN user_likes ul1
ON uf.follower_id = ul1.user_id
LEFT JOIN user_likes ul2
ON uf.follower_id = ul2.user_id
AND ul1.comment_id < ul2.comment_id
WHERE uf.user_id = 1
GROUP BY ul1.user_id, ul1.comment_id
HAVING COUNT(ul2.comment_id) < 2
) AS tmp
GROUP BY tmp.follower_id
) AS tmp
LEFT JOIN user_follower uf2
ON tmp.follower_id = uf2.user_id
GROUP BY tmp.follower_id