How to create right query? - mysql

I'm trying write a query:
SELECT id FROM users WHERE status = 3
But if this sample returns an empty response, then I need instead to select the id where status = 4, and if it returns empty again, where status = 5.
How can I write a single query to solve this?

I think you simply want:
SELECT id
FROM users
WHERE status >= 3
ORDER BY status asc
LIMIT 1;
If you want multiple users:
SELECT u.id
FROM users u
WHERE u.status = (SELECT MIN(u2.status)
FROM users u2
WHERE u2.status >= 3
);
If you have a fixed list you want to test, you can also use:
select u.id
from users u
where u.status = 3
union all
select u.id
from users u
where u.status = 4 and
not exists (select 1 from users u2 where u2.status in (3))
union all
select u.id
from users u
where u.status = 5 and
not exists (select 1 from users u2 where u2.status in (3, 4));

You can use OR condition or use IN operator
SELECT id FROM users WHERE status = 3 or status = 3 or status = 5
or
SELECT id FROM users WHERE status IN (3,4,5)

I will use the case statement in the where clause:
select id
from users
where status = case when status = 3 and id is null then 4
when status = 4 and id is null then 5
else 3
end
Let me know if you have any question.

Assuming that your table look like this:
+----+--------+
| id | status |
+----+--------+
| 1 | 3 |
| 1 | 4 |
| 1 | 5 |
| 3 | 3 |
| 3 | 4 |
| 4 | 4 |
| 4 | 5 |
| 5 | 5 |
+----+--------+
And based on your condition where you want to see the lowest status first for each id, you can use MIN() operator.
So, from your original query:
SELECT id,MIN(status) FROM users GROUP BY id;
Then you'll get a result like this:
+----+-------------+
| id | MIN(status) |
+----+-------------+
| 1 | 3 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
+----+-------------+

Related

Query to select from one table and then from the other, based on results

I have been trying to select all things from one table and then for each result select from another table only one newest result using MySQL.
The first table is a standard one, with AI id and text name
users
+----+-------+
| id | name |
+----+-------+
| 1 | John |
| 2 | Peter |
+----+-------+
then there is the second one with AI id, int user_id, text action and datetime date
actions
+----+---------+--------+------------+-----+
| id | user_id | action | date | str |
+----+---------+--------+------------+-----+
| 7 | 2 | drink | 2019-01-10 | 5 |
| 6 | 1 | sleep | 2019-02-14 | -2 |
| 5 | 2 | walk | 2019-04-24 | 4 |
| 4 | 1 | jump | 2019-03-14 | 3 |
| 3 | 2 | talk | 2019-04-30 | -8 |
| 2 | 2 | train | 2019-04-14 | -1 |
| 1 | 1 | drive | 2019-04-01 | 1 |
+----+---------+--------+------------+-----+
So now I want to select all from table_users and for each found row search table_actions and find only newest one, either based on id or date.
So it would look either like this (by id)
[0] => ['user_id'] = 1
['name'] = John
['action'] = sleep
['date'] = 2019-02-14
['str'] = -2
[1] => ['user_id'] = 2
['name'] = Peter
['action'] = drink
['date'] = 2019-01-10
['str'] = 5
or like this
[0] => ['id'] = 1
['name'] = John
['table_actions'] => ['id'] = 6
['user_id'] = 1
['action'] = sleep
['date'] = 2019-02-14
['str'] = -2
this sounds easy, but I tried few things and nothing worked like this. The closes one was with something like (dont have exact version on my hand, just remembering from on top of my head):
SELECT users.*, actions.*
FROM users
LEFT JOIN actions ON users.id = (
SELECT user_id FROM actions
WHERE users.id = actions.user_id
LIMIT 1
)
GROUP BY actions.user_id
with that I would get all results from users and then for each get one result from actions, but that result from actions would not be the newest one, apparently it groups as it likes to, I tried MAX(actions.id), but I have got no luck with that.
Does anyone know the solution ? for now I have to take all from users and for each result take another query in my php code and I feel there is an elegant and faster way to do that.
With this query:
select
user_id,
max(date) date
from actions
group by user_id
you get the latest date for each user and then you must join to it the 2 tables:
select
u.id, u.name, a.id, a.action, a.date, a.str
from users u
inner join actions a on a.user_id = u.id
inner join (
select
user_id,
max(date) date
from actions
group by user_id
) g on g.user_id = a.user_id and g.date = a.date
If you want the latest results by id and not by date:
select
u.id, u.name, a.id, a.action, a.date, a.str
from users u
inner join actions a on a.user_id = u.id
inner join (
select
user_id,
max(id) id
from actions
group by user_id
) g on g.user_id = a.user_id and g.id = a.id

How to find if two person following each other

This is my table following now i want to find the list of follower of user 1 that are also followed by user 1
id | user | follower
-----------------------
1 | 1 | 2
2 | 2 | 1
3 | 1 | 3
4 | 3 | 1
For example i want to find the list of my followers who are also following me
With EXISTS:
select t.follower from tablename t
where
t.user = 1
and exists (
select 1 from tablename
where user = t.follower and follower = t.user
)
See the demo.
Results
| follower |
| -------- |
| 2 |
| 3 |
You could self-join the table:
SELECT f1.user
FROM followers f1
JOIN followers f2 ON f1.user = f2.follower AND f1.follower = f2.user
WHERE f1.follower = 1 -- For a specific user

mysql LEFT JOIN main table and two additional tables with max value from each

So i have three tables:
Users
+-------+-----+----+
| id | val1|val2|
+-------+-----+----+
| 1 | 1 |3 |
| 2 | 2 |5 |
| 3 | 4 |7 |
+-------+-----+----+
UsersData
+----+--------------+------------+-----|
| id | users_id | created_at | gold|
+----+--------------+------------+-----|
| 9 | 1 |121454561212| 14 |
| 10| 1 |131454561212| 2 |
| 11| 2 |111454561212| 99 |
+----+--------------+------------+-----+
Extra
+----+------------+-----|
| id | users_id | val4|
+----+------------+-----|
| 1 | 1 | 5 |
| 2 | 1 | 6 |
| 3 | 1 | 7 |
+----+------------+-----+
So what i wish to achieve(in a single query) is to get a single row result for user with id = 1, that holds:
everything from Users Table
gold value of the most recent entry for that user (users_id = 1, created_at = MAX)
biggest val4 from the Extra table, where users_id = 1
So the result row would look like this:
+-------+-----+----+-----+----+
| id | val1|val2|gold |val4|
+-------+-----+----+-----+----|
| 1 | 1 |3 | 2 | 7 |
------------------------------+
I can get The first part done with
SELECT Users.id, Users.val1, Users.val2, UsersData.gold
FROM UsersData
LEFT JOIN Users ON UsersData.users_id = Users.id
WHERE Users.id = 1
ORDER BY UsersData.created_at DESC
LIMIT 1
and the second part with
SELECT MAX(Distances.distance) AS maxdistance FROM Distances WHERE Distances.users_id = 1
But i can't combine them no matter how i try... I would really like to have this done in single query, obviously i can do it with multiple - but i believe it is just my lack of mysql skills that is the issue here.
Thanks!
Just use subquery:
SELECT Users.id, Users.val1, Users.val2, UsersData.gold,
(SELECT MAX(Distances.distance) FROM Distances WHERE Distances.users_id = Users.id) AS maxdistance
FROM UsersData
RIGHT JOIN Users ON UsersData.users_id = Users.id
WHERE Users.id = 1
ORDER BY UsersData.created_at DESC
LIMIT 1
This is subquery connected by Users.id:
SELECT MAX(Distances.distance) FROM Distances WHERE Distances.users_id = Users.id) AS maxdistance
I would use subqueries like this:
select u.*,
(select ud.gold
from userdata ud
where ud.users_id = u.id
order by ud.created_at desc
limit 1
) as most_recent_gold,
(select max(e.val4)
from extra e
where e.users_id = u.id
) as max_val4
from users u
where u.id = 1 ;

How do I join multiple entries in one table into a single relationship status in MySQL?

I have a table Follow, which only holds records of which UserID follows which TargetID.
If asked for user A:
If neither A or B are following eachother, they have status of 0 for unrelated, and aren't included in the results.
If user A is following B but not vice versa, B has status 1 for
being followed.
If user B is following A but not vice versa, B has
status 2 for being a follower.
If A is following B, and B following
A, B has status of 3 for being a friend.
How can I, in a single MySQL query, get the relationship status for a given user and all its relationships above status 0?
Example:
Users:
+----+-------+
| id | Name |
+----+-------+
| 1 | Bob |
| 2 | Steve |
| 3 | Scott |
| 4 | Mary |
+----+-------+
Follow:
+----+--------+----------+
| id | UserID | TargetID |
+----+--------+----------+
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 2 | 1 |
| 4 | 4 | 1 |
+----+--------+----------+
Expected result for user 1:
+----------+--------+-------+
| TargetID | Status | Name |
+----------+--------+-------+
| 2 | 3 | Steve | (friend)
| 3 | 1 | Scott | (following)
| 4 | 2 | Mary | (follower)
+----------+--------+-------+
You can use subqueries as illustrated below:
-- FOR USER 1
SELECT A.id TargetID,
SUM(IFNULL((SELECT 1 C FROM Follow B WHERE B.UserID=1 AND B.TargetID=A.id),0) +
IFNULL((SELECT 2 C FROM Follow D WHERE A.id=D.UserID AND D.TargetID=1), 0)) Status
, A.name
FROM (SELECT * FROM Users WHERE ID<>1) A
GROUP BY A.id, A.Name
HAVING Status>0; -- for a compact result
-- NOW GLOBALLY
SELECT A.UserID, A.id TargetID,
SUM(IFNULL((SELECT 1 C FROM Follow B WHERE B.UserID=A.UserID AND B.TargetID=A.id),0) +
IFNULL((SELECT 2 C FROM Follow D WHERE A.id=D.UserID AND D.TargetID=A.UserID), 0)) Status
, A.name
FROM (SELECT E.id UserID, F.* FROM Users E JOIN Users F ON E.id<>F.id) A
GROUP BY A.UserID, A.id, A.Name
HAVING Status>0 -- for a compact result
ORDER BY A.UserID;
See DEMO on SQL Fiddle
I have not tried this but try something among the lines of:
Select t.targetid as TargetId,
IF (
(select count(id) from follow where
follow.Userid = f.target.id and follow.target_id = u.id) > 1,
-- mean’s the target is following user 1
(IF (
(select count(id) from follow where
follow.Userid = u.id and follow.target_id = f.targetid) > 1, 3, 2))
-- if user1 is following aswell, then its a friend, else its a follower
, 1)
-- else means its a following
as status,
u.name as Name from follow f
inner Join users u on u.id = f.targetid
where u.id = 1
Inner join to select user 1's relations (if it doesn't exist, they aren't related)
If there is a record, means they are one of 3:

SQL query to get my followers except me

I try to make a sql query to get all my followers except me.
I have the following tables:
users
| id_user | username |
| 1 | xaxa |
| 2 | toto |
| 3 | bubu |
| 4 | yiyi |
| 5 | pepe |
| 6 | sisi |
| 7 | fifi |
| 8 | mama |
| 9 | juju |
| 10 | cece | => me
friends
| id_friend | id_user | id_user_to |
| 1 | 10 | 1 |
| 2 | 2 | 10 |
| 3 | 2 | 1 |
| 4 | 6 | 3 |
| 5 | 2 | 9 |
| 6 | 6 | 7 |
| 7 | 5 | 3 |
| 8 | 10 | 5 |
| 9 | 9 | 8 |
| 10 | 8 | 10 |
I want to have this:
my friends
| id_user | name |
| 1 | xaxa |
| 2 | toto |
| 5 | pepe |
| 8 | mama |
actually I have id_user 10 (me) in the result with this query =>
SELECT id_user, name
FROM `users`
WHERE id_user NOT IN (
SELECT `id_user` FROM `friends`
WHERE ( `id_user` = 10 OR `id_user_to` = 10 ))
OR id_user NOT IN (
SELECT `id_user_to` FROM `friends`
WHERE ( `id_user` = 10 OR `id_user_to` = 10 ))
GROUP BY `id_user`
The solution is a union of two simple joins:
SELECT u.id_user, u.username name
FROM friends f
JOIN users u ON u.id_user = f.id_user_to
WHERE f.id_user = 10
UNION
SELECT u.id_user, u.username
FROM friends f
JOIN users u ON u.id_user = f.id_user
WHERE f.id_user_to = 10
Note that the keyword UNION removes duplicates from the result, so no need to code anything special to handle the case when there's a friend link in both directions between two users (FYI, UNION ALL retains duplicates).
Because at most one index is used per table per query, by splitting the query into two parts, if indexes are created on the user id columns of the friends table, this query will scale well (to millions of users)
There was no need to code anything to handle an "except me" condition, unless you have a row in the friends table for you being your own friend, which you don't.
SqlFiddle: http://sqlfiddle.com/#!9/7cbb3/4
This should do:
SELECT u.id_user, u.username name
FROM `friends` f
JOIN `users` u
ON u.id_user = f.id_user_to and f.id_user = 10
or u.id_user = f.id_user and f.id_user_to = 10
ORDER BY u.id_user
SqlFiddle: http://sqlfiddle.com/#!9/7cbb3/1
So essentially you need to get users who are related in either direction (id_user -> id_user_to OR id_user_to -> id_user)
You can do either one of those with these queries:
SELECT friends.id_user, users.name
FROM users
JOIN friends on users.id_user = friends.id_user
WHERE friends.id_user_to = 10
SELECT friends.id_user_to, users.name
FROM users
JOIN friends on users.id_user = friends.id_user
WHERE friends.id_user = 10
But you want both sides. One way to do it is to do both queries and UNION them together. You could do it like this whilst also adding in the names
SELECT friends.id_user, users.name
FROM users
JOIN friends on users.id_user = friends.id_user
WHERE friends.id_user_to = 10
UNION
SELECT friends.id_user_to, users.name
FROM users
JOIN friends on users.id_user = friends.id_user
WHERE friends.id_user = 10
It's also worth noting that the UNION will only show you distinct rows so if you have users in both directions (for example 1 -> 10 and 10 -> 1) they will not show twice.