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.
Related
I have two tables in MariaDB where I need to show those in the left table where their current score is not the same as the most recent score in the history table.
For example:
users
id name current_score
1 Bob 4
2 Tom 5
3 Fred 3
4 Tim 3
5 Ian 4
histories
id user_id score date
1 1 3 2018-11-13
2 1 4 2018-11-12
3 1 2 2018-11-11
4 2 5 2018-11-12
In the above I would want to show Bob as his latest history is not the same as his current score but not show Tom as his is a match
I tried using something like:
SELECT u.id, u.name, u.current_score
FROM users u
where u.current_score not in
(select h.score from histories h where
h.user_id=u.id order by h.date desc limit 1)
This threw an error:
#1235 - This version of MariaDB doesn't yet support
'LIMIT & IN/ALL/ANY/SOME subquery'
If I remove the limit 1 then it returns almost all the rows in users - there are a few thousands rows in each tables but I think it should return around 50 but it is returning over 4,100 rows out of 4,285 possible rows
Determine the latest history score in a Correlated subquery, within the Select clause itself.
Group By on the user, and use HAVING clause to consider cases where current score does not match the latest score in the history
I have to use MAX() aggregation function on the score values, so that it is a valid ANSI SQL compliant GROUP BY. It does not affect anything, as respective score values are one only (thus maximum only).
Try the following instead:
SELECT u.id,
u.name,
MAX(u.current_score) AS m_current_score,
MAX((select h.score
from histories h
where h.user_id = u.id
order by h.date desc limit 1)) AS history_score
FROM users u
GROUP BY u.id, u.name
HAVING m_current_score <> history_score
One approach for what you need is using a sub-query for get the date related to the latest history entry for every user_id. After this, you can join again the table histories for get the rest of the columns associated to this latest date. This is summarized on next query:
SELECT
u.id,
u.name,
u.current_score,
h.score AS latest_score_from_history
FROM
user AS u
LEFT JOIN
-- This subquery gets the date of the latest history register for every user
(SELECT
user_id,
MAX(date) AS maxDate
FROM
histories
GROUP BY
user_id) AS latest ON latest.user_id = u.id
LEFT JOIN
histories AS h ON h.user_id = latest.user_id AND h.date = latest.maxDate
WHERE
u.current_score <> h.score
Table name : users
I am trying to query this table such that it gives me a result showing - names for users - where - the last approved status - group by name - has manager_2 as Stan
Expected result image below
Something like
SELECT id,name,manager_1,manager_2,department_status_fruits
FROM users
WHERE status = --- the last approved entry for (group by Name),
if manager_2 = stan
ORDER BY id DESC
LIMIT 1
SELECT id,name,manager_1,manager_2,department_status_fruits
FROM users
WHERE manager_2 = 'stan' AND status = 'approved'
group by name
ORDER BY id DESC
LIMIT 1
None of the queries I tried are giving the expected results, infact I am finding it hard to even form a query to explain the request.
I guess, sub queries or joins is what i will have to go for, please suggest, joins is what I would prefer though.
You need to use a query that returns the last approved row for each name. See SQL select only rows with max value on a column for various ways to do this.
Then you can further filter this to those where manager_2 = 'stan'.
SELECT u1.*
FROM users AS u1
JOIN (SELECT name, MAX(id) AS maxid
FROM users
WHERE status = 'approved'
GROUP BY name) AS u2 ON u1.id = u2.id
WHERE manager_2 = 'stan'
ORDER BY id DESC
LIMIT 1
I have two tables: users and photos.
Users have many photos. Photos have a column called user_id, photos have one user. Photos also have a column called reported which is 0 or 1.
I need to know the number of users who have at least 1 photo with reported = 1. I also need to get the number of users who have at least 2 photos with reported = 1.
How would I do this? Here's what I'd like to do, but it obviously doesn't work:
select count(*)
from users join
(select * from photos where photos.reported = 1) as p2
on users.photo_id = p2.id;
This is at least 1
select count(distinct userid)
from photos
where reported = 1
This is at least 2.
select count(distinct userid)
from photos
where reported = 1
group by userid
having count(userid) > 2
Just get a histogram of the counts:
select numreported, count(*), min(user_id), max(user_id)
from (select p.user_id, sum(p.reported = 1) as numreported
from photos p
group by p.user_id
) p
group by numreported
order by numreported;
This gives you the number of users that have all counts of numreported, including 0.
Something like the following should work
select count(hasOne) cntHasOne, count(hasTwo) cntHasTwo from
(select users.user_id, 1 hasOne,
case when count(*) > 1 then 1 else 0 end hasTwo
from users inner join solution on(users.user_id = solution.user_id)
where solution.winning_status = 1
group by user_id) T1
I am creating a mobile application that need to synchronize with the server, and In order to do so, I need to get last (N) messages in each conversation.
note that this query was worked but get only last message in each conversation.
SELECT users.user_id AS user_id,
users.username,
users.picture,
users.last_seen,
me.message,
me.created_on
FROM messages me,
users
WHERE (me.sender_id=1
OR me.recipient_id=1)
AND ((me.sender_id=user_id
AND me.sender_id<>1)
OR (me.recipient_id=user_id
AND me.recipient_id<>1))
AND NOT exists
(SELECT 1
FROM messages me2
WHERE me2.id>me.id
AND ((me.sender_id=me2.sender_id
AND me.recipient_id=me2.recipient_id)
OR (me.sender_id=me2.recipient_id
AND me.recipient_id=me2.sender_id)))
ORDER BY me.created_on DESC
First we get all messages of all users ordered by user and date. We introduce artificial vars to number messages of user. When user id is the same we just increase message number. If it's different reset it to 0.
Thus subquery returns us
user_id, mess_n
1 0
1 1
1 2
2 0
2 1
2 2
3 0
3 1
3 2
Then in the query just leave messages with number <10 (first 10)
select *
from (
select u.*, m.*,
#mess_n_for_user:=if(u.user_id!=#curr_user,0,#mess_n_for_user+1) as mess_n,
#curr_user:=u.user_id
from (SELECT #mess_n_for_user:=0, #curr_user:=-1) sess_var,
users u join messages m on (u.user_id=m.sender_id
or u.user_id=m.recipient_id)
order by u.user_id, m.created_on DESC) all_messages_ordered
where all_messages_ordered.mess_n<10
Just add all filters conditions to the query
UPDATED
FROM the sqlfiddle
select * from (
select all_messages_ordered.*,
#mess_n_for_user:=if(u_id!=#curr_user,0,#mess_n_for_user+1) as mess_n,
#curr_user:=u_id
from (SELECT #mess_n_for_user:=0, #curr_user:=-1) sess_var,
(
select u.id as u_id, u.first_name, m.*
from
accounts u join messages m on (u.id=m.from or u.id=m.to)
and (m.to=1 or m.from=1)
and u.id<>1
order by u.id, m.date_time DESC) all_messages_ordered) a
where mess_n<3
You need a recursive query to select conversations. Following the approach from this answer, substituting recipient_id / sender_id for col3 / col1 :
select id, recipient_id, #pv:=sender_id as 'recipient_id' from messages
join
(select #pv:=2)tmp
where recipient_id=#pv
I have not tested this but the principle should be correct. Of course you will need to expand this to fully solve your problem...but hope this helps.
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.