database query help wanted - mysql

Is it possible to limit the results a mysql select query returns with a condition?
For example I have a reviews table:
review_id, member_id, text, date
And I'd like to get the latest 10 reviews but member_id = 123 should only be taken once
Can this be achieved with a single query?

My interpretation of the problem:
the 10 most recent reviews
including at most 1 review with member_id = 123
I'm going to solve this by:
starting with the full reviews result set
removing all reviews that have member_id = 123 except for the most recent one
from the modified result set, take the 10 most recent
Here's the query:
create view newest123 as ( -- this gets the newest review for member_id 123
select *
from reviews
where member_id = 123
order by date desc limit 1
)
select *
from (
select * from newest123
union
select * -- all the reviews that aren't for member_id 123
from reviews
where member_id != 123) filtered
order by date desc limit 10 -- sort 'em and take the top 10

Related

Get the next max(id)

I've got the following table:
booking_id
user_id
11
1
12
76
13
932
14
1
15
626
16
1
17
3232
I want to access the 2nd maximum booking_id for user 1.
The expected result is user_id = 1, booking_id = 14.
I've been working over these hellish flames for way too long, this doesn't do any good:
select booking.user_id, b1.booking_id from booking
left join(select
user_id,
booking_id
from booking
where booking_id = (select
max(booking_id)
from booking
where booking_id <> (select
max(booking_id)
from booking))
group by user_id)
as b1 on b1.user_id = booking.user_id
where booking.user_id = '1'
Please note I've managed to do it as a calculated column but that's useless, I need the derived table.
If you are using MySQL, you can avoid the (rather messy) double sub-query by using LIMIT & OFFSET
Just add order by booking_id desc LIMIT 1 OFFSET 1 and you will get the second highest booking_id. For example ...
select * from booking where user_id = 1 order by booking_id desc OFFSET 1 LIMIT 1
I tested this on one of my tables & it worked fine. If you have an index on booking_id it should be really fast.
If you want the second highest booking for the user who holds the highest booking, then this should work
SELECT * FROM booking
WHERE user_id in
(select user_id from booking order by booking_id desc limit 1)
ORDER BY booking_id DESC LIMIT 1 OFFSET 1
The sub-query finds the user_id of the user with the highest booking, then the main query finds their second highest booking
A simple way to do it is using LIMIT OFFSET:
SELECT *
FROM booking
WHERE user_id = 1
ORDER BY booking_id DESC
LIMIT 1 OFFSET 1
Demo here
By using the answer in this question What is the simplest SQL Query to find the second largest value? https://stackoverflow.com/a/7362165/14491685
you can integrate with your query to get it like this:
select * from booking
where booking_id =
(select max(booking_id) from booking
where user_id =1
and booking_id not in (SELECT MAX(booking_id ) FROM booking ))

Advanced mysql query which sort down specific records on result set irrespective of its default sorting?

I have a query which actually have a sorting using order by clause. i have a table like following...
user_id user_name user_age user_state user_points
1 Rakul 30 CA 56
2 Naydee 29 NY 144
3 Jeet 40 NJ 43
.....
i have following query...
select * from users where user_state = 'NY' order by user_points desc limit 50;
This gives me the list of 50 people with most points. I wanted to give least preference to few people who's id's were known. Incase if i do not have enough 50 records then those id's should come in the last in the list. I do not want the users 2 and 3 to come on top of the list even though they have higher points... those people should come on the last of the list from the query. Is there any way to push specific records to last on result set irrespective of query sorting ?
If you want to move specific records (like user_id = 2 and 3) down to the list; Then you can run below Query:
mysql> select *,IF(user_id=2 or user_id=3,0,1) as list_order from users where user_state = 'NY' order by list_order desc, user_points desc limit 50;
select * from (
select *
from users
where user_state = 'NY'
-- this order by ensures that 2 and 3 are included
order by case when user_id in (2,3) then 1 else 2 end, user_points desc
limit 50
) as top48plus2n3
-- this order by ensures that 2 and 3 are last
order by case when user_id in (2,3) then 2 else 1 end, user_points desc
Edit: changed id by user_id and corrected outside order by (sorry about that)
On the inner select:
By using this case calculation, what you do is ensuring that records with ids equal to 2 and 3 are "important" (firstly ordered in the order by). Those receive 1 while the others receive 2 as order value, only after that points are relevant.
On the outer select:
Records with ids 2 and 3 recieve 2 as order value, while the rest recieve 1. So they go last irrespective of its "default"
Here you have a reduced fiddle http://sqlfiddle.com/#!9/377c1/1

MySQL Query to find row duplicates based on condition with limit

I have two tables:
Members:
id username
Trips:
id member_id flag_status created
("YES" or "NO")
I can do a query like this:
SELECT
Trip.id, Trip.member_id, Trip.flag_status
FROM
trips Trip
WHERE
Trip.member_id = 1711
ORDER BY
Trip.created DESC
LIMIT
3
Which CAN give results like this:
id member_id flag_status
8 1711 YES
9 1711 YES
10 1711 YES
My goal is to know if the member's last three trips all had a flag_status = "YES", if any of the three != "YES", then I don't want it to count.
I also want to be able to remove the WHERE Trip.member_id = 1711 clause, and have it run for all my members, and give me the total number of members whose last 3 trips all have flag_status = "YES"
Any ideas?
Thanks!
http://sqlfiddle.com/#!2/28b2d
In that sqlfiddle, when the correct query i'm seeking runs, I should see results such as:
COUNT(Member.id)
2
The two members that should qualify are members 1 and 3. Member 5 fails because one of his trips has flag_status = "NO"
You could use GROUP_CONCAT function, to obtain a list of all of the status ordered by id in ascending order:
SELECT
member_id,
GROUP_CONCAT(flag_status ORDER BY id DESC) as status
FROM
trips
GROUP BY
member_id
HAVING
SUBSTRING_INDEX(status, ',', 3) NOT LIKE '%NO%'
and then using SUBSTRING_INDEX you can extract only the last three status flags, and exclude those that contains a NO. Please see fiddle here. I'm assuming that all of your rows are ordered by ID, but if you have a created date you should better use:
GROUP_CONCAT(flag_status ORDER BY created DESC) as status
as Raymond suggested. Then, you could also return just the count of the rows returned using something like:
SELECT COUNT(*)
FROM (
...the query above...
) as q
Although I like the simplicity of fthiella's solution, I just can't think of a solution that depends so much on data representation. In order not to depend on it you can do something like this:
SELECT COUNT(*) FROM (
SELECT member_id FROM (
SELECT
flag_status,
#flag_index := IF(member_id = #member, #flag_index + 1, 1) flag_index,
#member := member_id member_id
FROM trips, (SELECT #member := 0, #flag_index := 1) init
ORDER BY member_id, id DESC
) x
WHERE flag_index <= 3
GROUP BY member_id
HAVING SUM(flag_status = 'NO') = 0
) x
Fiddle here. Note I've slightly modified the fiddle to remove one of the users.
The process basically ranks the trips for each of the members based on their id desc and then only keeps the last 3 of them. Then it makes sure that none of the fetched trips has a NO in the flag_status. FInally all the matching meembers are counted.

MySQL select top ten records with no duplicate uid

I have the following table (user_record) with millions of rows like this:
no uid s
================
1 a 999
2 b 899
3 c 1234
4 a 1322
5 b 933
-----------------
The uid can be duplicate .What I need is to show the top ten records(need inclued uid and s) with no duplicate uid order by s (desc). I can do this by two steps in the following SQL statements:
SELECT distinct(uid) FROM user_record ORDER BY s DESC LIMIT 10
SELECT uid,s FROM user_record WHERE uid IN(Just Results)
I just wana know is there a bit more efficient way in one statement?
Any help is greatly appreciated.
ps:I also have following the SQL statement:
select * from(select uid,s from user_record order by s desc) as tb group by tb.uid order by tb.s desc limit 10
but it's slow
The simpliest would be by using MAX() to get the highest s for every uid and sorted it based on the highest s.
SELECT uid, MAX(s) max_s
FROM TableName
GROUP BY uid
ORDER BY max_s DESC
LIMIT 10
SQLFiddle Demo
The disadvantage of the query above is that it doesn't handles duplicates if for instance there are multiple uid that have the same s and turn out to be the highest value. If you want to get the highest value s with duplicate, you can do by calculating it on the subquery and joining the result on the original table.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT DISTINCT s
FROM TableName
ORDER BY s DESC
LIMIT 10
) b ON a.s = b.s
ORDER BY s DESC

MYSQL query to select from multiple tables, order by date, different table structures

I'm making a newsfeed type of thing and I want to select from multiple tables. The two tables I'll focus on for this question are "posts" and "photos".
Here's my query for just posts:
mysql_query("
SELECT * FROM posts
WHERE toID='$id' AND state='0'
ORDER BY id DESC LIMIT 10");
My posts table has the following column names:
Table: posts
id toID fromID post state date
1 1 1 Aloha 0 1
2 1 1 Hello 0 3
My photos table has the following:
Table: photos
id userID photo state date
1 1 2 0 2
2 1 6 0 4
Expected result:
Aloha
2
Hello
6
Maybe something like:
SELECT *
(SELECT * FROM posts WHERE toID=$id AND state=0) AND
(SELECT * FROM photos WHERE userID=$id AND state=0)
ORDER BY date
When it selects these from the database it should select from where toID and userID are the same. state should equal 0 for both, (0 means visible) and they should be ordered by date. Also I need to create a new variable to pass to my query, so I can then in my php determine which table the information is coming from. Lastly I would like it to group the photos by date, so let's say a user uploaded 20 photos within a 30 minute period, they will only return one row. I use php time() to store my date.
If you want to get all posts and photos together you could use:
SELECT po.*, ph.* FROM posts po
LEFT JOIN photos ph
ON po.toID = ph.userID
WHERE po.state = 0
AND ph.state = 0
ORDER BY po.id DESC, ph.date DESC