SQL query NOT EXISTS intesad of NOT IN - mysql

I'm having issues finding an appropriate NOT EXISTS-Query to replace my NOT IN one. I have to replace it because of the nullable-columns problem. However I'm rather new to SQL, so I can't get the right relations.
What I'm trying to do is to exclude all users that are already assigned to a practice, which do not have the "informal" flag set (in other words: Unassigned users with the informal flag). My current query is
SELECT USERS.UserID, USERS.Name
FROM USERS
WHERE USERS.Informal = true AND USERS.UserID NOT IN (
SELECT USERS.UserID
FROM PRACTICES
LEFT JOIN ATTENDS ON PRACTICES.PracticeID = ATTENDS.PracticeID
LEFT JOIN USERS ON ATTENDS.UserID = USERS.UserID
WHERE PRACTICES.PracticeID = 7
)
this is the relevant table structure for the query
I'd be very grateful if someoune could hint me in the right direction.

There is no ned to repeat the USERS table in the subquery -- for either NOT IN or NOT EXISTS. I strongly recommend NOT EXISTS (because it handles NULL values more intuitively):
SELECT u.UserID, u.Name
FROM USERS u
WHERE u.Informal = true AND
NOT EXISTS (SELECT USERS.UserID
FROM PRACTICES p
ATTENDS a
ON p.PracticeID = a.PracticeID
WHERE a.UserID = u.UserID AND p.PracticeID = 7
)

Related

Left join sql query

I want to get all the data from the users table & the last record associated with him from my connection_history table , it's working only when i don't add at the end of my query
ORDER BY contributions DESC
( When i add it , i have only the record wich come from users and not the last connection_history record)
My question is : how i can get the entires data ordered by contributions DESC
SELECT * FROM users LEFT JOIN connections_history ch ON users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date)
The order by should not affect the results that are returned. It only changes the ordering. You are probably getting what you want, just in an unexpected order. For instance, your query interface might be returning a fixed number of rows. Changing the order of the rows could make it look like the result set is different.
I will say that I find = to be more intuitive than EXISTS for this purpose:
SELECT *
FROM users u LEFT JOIN
connections_history ch
ON u.id = ch.guid AND
ch.date = (SELECT Max(ch1.date)
FROM connections_history ch1
WHERE ch.guid = ch1.guid
)
ORDER BY contributions DESC;
The reason is that the = is directly in the ON clause, so it is clear what the relationship between the tables is.
For your casual consideration, a different formatting of the original code. Note in particular the indented AND suggests the clause is part of the LEFT JOIN, which it is.
SELECT * FROM users
LEFT JOIN connections_history ch ON
users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date
)
We can use nested queries to first check for max_date for a given user and pass the list of guid to the nested query assuming all the users has at least one record in the connection history table otherwise you could use Left Join instead.
select B.*,X.* from users B JOIN (
select A.* from connection_history A
where A.guid = B.guid and A.date = (
select max(date) from connection_history where guid = B.guid) )X on
X.guid = B.guid
order by B.contributions DESC;

MySQL: what is the most efficient way to retrieve all permissions from RBAC database?

Good day.
I have a role-based access control database and looking for an efficient way to get all permissions user has. This is the schematics:
Now I need to get all user's permission in the most (possibly) efficient way. Tried this query:
SELECT p.name
FROM permission p
WHERE p.id = (
SELECT rpl.permission_id
FROM role_permission_list rpl
WHERE rpl.role_id = (
SELECT url.role_id
FROM user_role_list url
WHERE url.user_id = 2
)
)
But this fails. Since sub-query returns more than 1 result. Tried to think of a join — couldn't figure it out.
Thanks in advance.
PS: In future there will be an permission_overrides table, since one standalone access-controlled entity may have exclusive set of permissions.
It will be simple 3 joins, like:
SELECT
`user`.id,
permission.*
FROM
`user`
LEFT JOIN user_role_list ON `user`.id=user_role_list.user_id
LEFT JOIN role_permission_list ON user_role_list.role_id=role_permission_list.role_id
LEFT JOIN permission ON role_permission_list.permission_id=permission.id
WHERE
`user`.id=$user_id
(since you're not looking for role description from which specific permission came, I've not include that table to query)
Give this a try:
SELECT u.id, p.name
FROM `user` u
LEFT JOIN user_role_list url ON u.id = url.user_id
LEFT JOIN role r ON r.ID = url.role_id
LEFT JOIN role_permission_list rpl ON rpl.role_id = r.id
LEFT JOIN permission p ON p.ID = rpl.permission_id
WHERE u.id = A_Users_ID

mysql query with either OR and right join on result of either or

I'm writing an internal/private messaging/email system for a new site and so far its been going very nicely but I just hit a snag... the trash folder.
I need to do a query that includes all records where fields (a AND b) = one setting OR (x AND y) = another setting and then right join that record on the user table so that I can tell who originated the record/message.
Here's what I have:
SELECT * FROM mail AS M
RIGHT JOIN users AS U on U.userid = M.frm_userid or M.to_userid /* result of WHERE */
WHERE (frm_userid='$userid' AND frm_status=3) OR (to_userid='$userid' AND to_status=3)
ORDER BY some_date_field DESC
I'm stuck on the RIGHT JOIN ON U.userid = I have no clue! I'm sure there's a way to do this but I just don't know MYSQL quite well enough to pull this one off!
Any suggestions?
Thanks,
Pete
Separate the TO and FROM processing into different parts of a UNION:
SELECT M.to_userid FROM mail AS M
JOIN users AS U on U.userid = M.frm_userid and M.frm_status=3
WHERE M.frm_userid = '$userid'
UNION
SELECT M.frm_userid FROM mail as M
JOIN users AS U on U.userid = M.to_userid and M.to_status=3
WHERE M.to_userid = '$userid'
ORDER BY some_date_field DESC
Try this
SELECT
*
FROM
mail AS M
RIGHT JOIN
users AS U on (U.userid = M.frm_userid or U.userid = M.to_userid)
WHERE
(frm_userid='$userid' AND frm_status=3) OR (to_userid='$userid' AND to_status=3)
ORDER BY
some_date_field DESC

Select column if not NULL then select other

I'm trying to figure how to return a row with the associated value when using the JOIN syntax, firstly it should check the links table to see if the row is NULL, if it's not NULL use that for the JOIN e.g. ON links.role_id = roles.role_id if the row is NULl. If it's NULL then use the people table.
My syntax:
SELECT roles.role_name
FROM
(
people_links links
LEFT OUTER JOIN people people ON links.person_id = people.person_id
LEFT OUTER JOIN roles roles ON links.role_id = roles.role_id OR links.role_id = people.role_id
)
Basically when joining and using the ON syntax with the role_id, it will return the associated value from the roles table that is used from the role_id so it should rely on the links table first, check if links.role_id is NULL then return people.role_id else links.role_id for the JOIN to get the role_name.
When I run this query, it uses the role_id from the people table first, when it should look in the links table first.
I tried using COALESCE but that will only return the role_id when it should return role_name, so for example (pseudo code):
SELECT IF(links.role_id IS NOT NULL) links.role_id ELSE people.role_id ENDIF as join_value
FROM
(
people_links links
LEFT OUTER JOIN people people ON links.person_id = people.person_id
LEFT OUTER JOIN roles roles ON roles.role_id = join_value
)
You could write IF(links.role_id IS NOT NULL, links.role_id, people.role_id) — that's the closest thing to your pseudocode — but it's more idiomatic to write COALESCE(links.role_id, people.role_id).
For information on IF, see §11.4 "Control Flow Functions" in the MySQL Reference Manual; for information on COALESCE, see §11.3.2 "Comparison Functions and Operators".
I think for code clarity that I would have two joins onto role. One from the link to role and one from the people to role. I.e.
SELECT
COALESCE(links_roles.role_name, people_roles.role_name)
FROM
(
people_links links
LEFT OUTER JOIN people people ON links.person_id = people.person_id
LEFT OUTER JOIN roles link_roles ON links.role_id = link_roles.role_id
LEFT OUTER JOIN roles people_roles ON people.role_id = people_roles.role_id
)

Simple MySQL Join problem

Im stumped by this simple query because its one I have not tried before.
Ive got a User table, User_Widget table and a Widget table.
A simple inner join shows me what widgets they have by joining user_widget.user_id = user.user_id.
How would I show the widgets in the Widget table that they dont have?
Look up WHERE NOT EXISTS with a subselect in your documentation..
Use a CROSS JOIN and a LEFT OUTER JOIN ( this is from my MS SQL experience, but the concept should hold ).
It works like this. The sub-query gets all possible combinations of user and widget.
The LEFT OUTER JOIN brings your User_Widgets associations into play.
The IS NULL part of the WHERE CLAUSE will exclude widgets that the user does have, giving you only the ones that don't.
SELECT allpossible.User_ID, allpossible.Widget_ID FROM
(
SELECT User_ID, Widget_ID FROM
Users
CROSS JOIN
Widgets
) allpossible
LEFT OUTER JOIN
User_Widgets uw
ON
allpossible.User_ID = uw.User_ID
AND allpossible.Widget_ID = uw.Widget_ID
WHERE
uw.UserID IS NULL
SELECT * FROM widgets WHERE id NOT IN
(
SELECT widget_id FROM user_widgets WHERE user_id = 1
)
(where 1 is the id of the user you're interested in)
This is a guess, (I haven't tried it), but try This:
Select Distinct u.*, Z.*
From User u
Left Join
(Select u.UserId, w.*
From Widget w
Where Not Exists
(Select * From User_Widget
Where userId = u.UserId
And widgetId = w.WidgetId)) Z
On Z.userId = u.UserId
Thanks to Bart Janson I got the query down to:
SELECT * FROM widgets
WHERE NOT EXISTS (
SELECT * FROM widget_user
WHERE widgets.widget_id = widget_user.widget_id
AND user_id = "ID NUMBER OF PERSON YOU WANT"
)
ORDER BY RAND()
LIMIT 10
Cheers guys
SELECT *
FROM widgets w
LEFT OUTER JOIN user_widget uw
ON w.id = uw.widget_id AND uw.user_id = 1 // or whatever user u want
WHERE uw.widget_id IS NULL;