SQL query, select from table1 order by table2 rows - mysql

I am trying to select a list of friends from a table, which works fine. However now I am trying to order the results by another table, which is the users online status/recent online activity.
The query I am trying to use is (The error is: Unknown column 'friendID' in 'where clause'):
SELECT
CASE WHEN f.userID=$session
THEN f.userID2 ELSE f.userID
END AS friendID
FROM friends f, usersStatus u
WHERE
(
f.userID=$session OR f.userID2=$session
)
AND friendID=u.userID
AND f.state='1'
ORDER BY u.periodicDate DESC, u.setStatus ASC
LIMIT 5
now the query that just returns the friends of a user is this:
SELECT
CASE WHEN f.userID=$session
THEN f.userID2 ELSE f.userID
END AS friendID
FROM friends f
WHERE
(
f.userID=$session OR f.userID2=$session
)
AND f.state='1'
LIMIT 5
The friendID should match usersStatus.userID, I then want to order them in the way in the example above. I would also like to add that if the friendID is not IN usersStatus table, it should automatically order those rows at the end.
u.periodicDate is just the most recent date set by the user, so this would indicate the last time the user was active, so I want the most recently active users to be first.
u.setStatus is set depending on what my script determines him to be, so it can be 1, 2, or 3. 1 being Online, 2 being Away, and 3 being Offline.
So like I said above, users that don't yet have a row in this table should automatically be given an offline status.
EDIT
I have got a query to work as I want it to, however It's extremely messy and must be hugely wasteful of its resources, as I have to GROUP BY friendID in order for it to return users only once. (because my friends database keeps every row, so if a person unfriends someone, it just creates a new row if they return friends etc.)
SELECT * FROM usersStatus,
(SELECT
CASE WHEN friends.userID=$session
THEN friends.userID2 ELSE friends.userID
END AS friendID
FROM friends, usersStatus
WHERE
(
friends.userID=$session OR friends.userID2=$session
)
AND friends.state='1'
) t
WHERE t.friendID=usersStatus.userID
GROUP BY t.friendID
ORDER BY usersStatus.periodicDate ASC, usersStatus.setStatus ASC
LIMIT 5

Instead of writing
AND friendID=u.userID
use the actual value for the alias:
AND CASE WHEN f.userID=$session
THEN f.userID2 ELSE f.userID
END = u.userID

Related

getting values from select with case/if statement depending on condition

I am stuck last 2 hours trying to figure out how to make this query or to find an example, till now no result in searching, so I will post here what is my problem. I have a group of users and there is a specific order in which I need to get values.
So group 1 is administrator and he can see all groups, for him the query to get values would be
SELECT g1.group_id,g1.description FROM groups g1 ORDER BY g1.description
Other groups can see all groups except Administrator group (id in this case is 1), for other users the query to get the values would be:
SELECT g2.group_id,g2.description FROM groups g2 WHERE g2.group_id > 1 ORDER BY g2.description
I tried to make something with CASE statement but this is not working and I know that subquery cant return more than 1 row, i get an error like this
[Err] 1242 - Subquery returns more than 1 row
My query is till now, the wrong one:
SELECT
CASE WHEN (SELECT ug.group_id FROM users_groups ug
WHERE ug.login="admin" and ug.group_id = 1)
THEN (SELECT g1.group_id,g1.description FROM groups g1 ORDER BY g1.description)
ELSE (SELECT g2.group_id,g2.description FROM groups g2 WHERE g2.group_id > 1 ORDER BY g2.description)
END
So I need results depending on the group of logged user, if user is admin and his group=1 than he can see all groups, in other way all groups except group 1, admin
any help would be appreciated, the most important thing is that I get the values in this order and only these two g1.group_id,g1.description or g2.group_id,g2.description
Desired output would be :
For user Administrator-
group_id description
1 Administrator
2 Customer
3 Supplier
For users that are not Administrator-
group_id description
2 Customer
3 Supplier
SELECT g.group_id, g.description
FROM groups AS g
CROSS JOIN user_groups AS ug
WHERE ug.login = #username
AND (ug.group_id = 1 OR g.group_id > 1)
ORDER BY g.description
#username is a placeholder for the username performing the query.

MySQL search users, order friends above others

I have a feature to search for users on my website,
I also have a feature for friends on my site.
I have a query to search for the correct users on my site, and
I have a query that determines the users that a user is friends with, they both work as they should,
so what i need assistance with is combining the two, to order friends before other users.
Search query:
SELECT userID, fname, lname,
FROM names
WHERE (
fname LIKE '".$term."%'
OR lname LIKE '".$term."%'
OR CONCAT(fname, ' ', lname) LIKE '".$term."%'
)
AND userID!=$session
ORDER BY fname ASC, lname ASC
LIMIT 10
Friends query:
SELECT CASE WHEN userID=$session
THEN userID2
ELSE userID
END AS friendID
FROM friends
WHERE (
userID=$session
OR userID2=$session
)
AND state='1'
so, a statement that would compare the two should be something like, IF names.userID=friends.friendID
Now I found a users suggestion for a similar issue on stackoverflow that seems like it would be a simple way of doing this, however I'm not entirely sure how to add it with my particular friends query, as it's more of a complex query.
SELECT
posting.id,
IF(a.friend_id = b.criteria, 1, 0) AS is_friend
...
ORDER BY is_friend DESC, posting.id DESC
Use an outer join (left join) to join the user list returned by your first query with the friend list that your second query produces. Now you'll be able to sort on the fact whether the second row set has a match: if it does, the corresponding row should go first.
This is an example of how you could implement the above:
SELECT
n.userID,
n.fname,
n.lname
FROM names n
LEFT JOIN (
SELECT CASE WHEN userID=$session
THEN userID2
ELSE userID
END AS friendID
FROM friends
WHERE (
userID=$session
OR userID2=$session
)
AND state='1'
) f
ON n.userID = f.friendID
WHERE (
n.fname LIKE '".$term."%'
OR n.lname LIKE '".$term."%'
OR CONCAT(n.fname, ' ', n.lname) LIKE '".$term."%'
)
AND n.userID!=$session
ORDER BY (f.friendID IS NULL), n.fname ASC, n.lname ASC
LIMIT 10
The first sorting criterion, (f.friendID IS NULL), evaluates to false if there's a matching f row, i.e. if the user is a friend, and to true otherwise. Consequently, since false sorts before true, the friends will be sorted before non-friends.

MySQL nested query counting

A bit of background info; this is an application that allows users to created challenges and then vote on those challenges (bog standard userX-vs-userY type application).
The end goal here is to get a list of 5 users sorted by the number of challenges they have won, to create a type of leaderboard. A challenge is won by a user if it's status = expired and the user has > 50 votes for that challenge (challenges expire after 100 votes in total).
I'll simplify things a bit here, but essentially there are three tables:
users
id
username
...
challenges
id
issued_to
issued_by
status
challenges_votes
id
challenge_id
user_id
voted_for
So far I have an inner query which looks like:
SELECT `challenges`.`id`
FROM `challenges_votes`
LEFT JOIN `challenges` ON (`challenges`.`id` = `challenges_votes`.`challenge_id`)
WHERE `voted_for` = 1
WHERE `challenges`.`status` = 'expired'
GROUP BY `challenges`.`id`
HAVING COUNT(`challenges_votes`.`id`) > 50
Which in this example would return challenge IDs that have expired and where the user with ID 1 has > 50 votes for.
What I need to do is count the number of rows returned here, apply it to each user from the users table, order this by the number of rows returned and limit it to 5.
To this end I have the following query:
SELECT `users`.`id`, `users`.`username`, COUNT(*) AS challenges_won
FROM (
SELECT `challenges`.`id`
FROM `challenges_votes`
LEFT JOIN `challenges` ON (`challenges`.`id` = `challenges_votes`.`challenge_id`)
WHERE `voted_for` = 1
GROUP BY `challenges`.`id`
HAVING COUNT(`challenges_votes`.`id`) > 0
) AS challenges_won, `users`
GROUP BY `users`.`id`
ORDER BY challenges_won
LIMIT 5
Which is kinda getting there but of course the voted_for user ID here is always 1. Is this even the right way to go about this type of query? Can anyone shed any light on how I should be doing it?
Thanks!
I guess the following script will solve your problem:
-- get the number of chalenges won by each user and return top 5
SELECT usr.id, usr.username, COUNT(*) AS challenges_won
FROM users usr
JOIN (
SELECT vot.challenge_id, vot.voted_for
FROM challenges_votes vot
WHERE vot.challenge_id IN ( -- is this check really necessary?
SELECT cha.id -- if any user is voted 51 he wins, so
FROM challenges cha -- why wait another 49 votes that won't
WHERE cha.status = 'expired' -- change the result?
) --
GROUP BY vot.challenge_id
HAVING COUNT(*) > 50
) aux ON (aux.voted_for = usr.id)
GROUP BY usr.id, usr.username
ORDER BY achallenges_won DESC LIMIT 5;
Please allow me to propose a small consideration to the condition to close a challenge: if any user wins after 51 votes, why is it necessary to wait another 49 votes that will not change the result? If this constraint can be dropped, you won't have to check challenges table and this can improve the query performance -- but, it can worsen too, you can only tell after testing with your actual database.

MySQL select comments for friends then other comments while chronological order

This question could asked somewhere before but I could not find it, anyways.
So I have a video record that has many comments, each comment belongs to a user. The user on the other hand has many user friends.
Now, when user X signs in and view video Y I want to display the comments of his friends first (ordered by most recent). Then list other comments ordered with most recent.
I would prefer to make this logic in one single query if possible.
Thanks,
In your ORDER BY, do something like the following. Please know that I have know idea what your schema looks like, so this query won't work. But just create a 1/0 value indicating whether or not the user is a friend, and then order by that first, and then order by the posting / comment id afterwards
SELECT
posting.id,
IF(a.friend_id = b.criteria, 1, 0) AS is_friend
...
ORDER BY is_friend DESC, posting.id DESC
How about this
Select comment of friends of X on video Y order by date
Union
Select comment on video Y where they is not friend of X order by date
Use UNION.
(
SELECT 1 AS sort,*
FROM comments
INNER JOIN friends ON comments.poster_id=friends.friend_id
WHERE friends.id='123'
)
UNION
(
SELECT 2 AS sort,*
FROM comments
WHERE poster_id NOT IN(SELECT friend_id FROM friend WHERE id='123')
)
ORDER BY sort,time DESC

How to restrict access to rows of my query based on a contacts table of user ids

I have a query that produces a result like this:
The data is sorted by date DESC, then by time DESC, but also the common 'markers_name' elements are chunked together. (They're only chunked together for each date).
To do this I get the list of MAX(time) values for every combination of (markers_id, date) in conditions, then join that list to the row set I am getting from the present query, and use the MAX(time) values for sorting:
SELECT
m.name AS markers_name,
c.time AS conditions_time,
c.date AS conditions_date,
s.name AS station_name
FROM markers m
INNER JOIN conditions c ON c.markers_id = m.id
INNER JOIN station s ON c.station_id = s.id
INNER JOIN (
SELECT
markers_id,
date,
MAX(time) AS time
FROM conditions
GROUP BY
markers_id,
date
) mx ON c.markers_id = mx.markers_id AND c.date = mx.date
ORDER BY
c.date DESC,
mx.time DESC,
m.name DESC,
c.time DESC
Now I need to restrict user access to some of the rows.
The conditions table has a 'private' column. If 'private' is set to 1 only some people are allowed to see the query row. The people that can see it include the person that created the conditions report and that person's contacts. The conditions table has a 'user_id', which contains the id of the person that created the conditions report. The contacts are obtained from a 'contacts' table which has two fields: 'user_id' and 'friend_id'. I have the user_id of the person requesting the information.
To restate another way, I need to do something like this:
Do the query above, but also check to see if c.private is set to one.
If c.private is set to one use c.user_id to get that user's 'friend_id's from the contacts table.
If the user_id of the person requesting the information matches c.user_id or any of c.user_id's friends return the query row. If not, don't return that row or return something that indicates that the row is private (I could have a markers_name with the name "Private" in the database, for example.
If anyone has any ideas I would love to hear them. I struggled with this all day yesterday. Thank you.
Possibly just adding a filter like this would be enough:
…
WHERE c.Private <> 1
OR c.user_id = #user_id
OR c.user_id IN (
SELECT friend_id
FROM contacts WHERE user_id = #user_id
)
where #user_id is the ID of the person requesting the info.
This would prohibit the private rows from appearing in the output where inappropriate.
If, however, you'd rather like them to be there but be marked as Private (for being dealt with later in the client or something like that), you could use that filter as a column in your SELECT clause:
SELECT
NOT (
c.Private <> 1
OR c.user_id = #user_id
OR c.user_id IN (
SELECT friend_id
FROM contacts WHERE user_id = #user_id
)
) AS IsPrivate,
…
FROM …
I don't have similar tables to play around with, so I'm writing this for you to try out, alright? Here is the WHERE clause for you to add to your query:
WHERE
-- all non-1 valued `private` columns are public, right? remove if not
c.private <> 1
-- all private but #user_id is entitled to view
OR (
c.private = 1
AND (
(c.user_id = #user_id) -- requestor is creator
OR (
EXISTS (
SELECT ct.friend_id
FROM contacts ct
WHERE ct.user_id = c.user_id
AND friend_id = #user_id
) -- requestor is a friend of the creator
)
)
)
Again, I wasn't able to test this.. I'll leave that up to you for now.. so please let me know how it goes. :)