Proper Join Query - mysql

I have a table that has every group (gid) a user (uid, say 1031, for example) belongs to and another table that has every group that has permission to edit a given document (pid). I'm trying to figure out the most efficient query to confirm if the user has the permission to edit the said document (e.g. if the user is in any group that is listed as having editing privileges). It seems like there are usually performance issues in JOIN queries I don't anticipate up front.
This is what I came up with. Is there a better way to do this?
SELECT p.gid
, p.pid
, p.r
, p.w
FROM permissions p
JOIN groups_members m
ON m.gid = p.gid
WHERE groups_members.uid = 1

Related

MySQL Query Inner Join Inverse?

How can we show the 'inverse' of the Inner Join. For example, I have a list of actual transactions of customers that went thru the payment processor, in this case 'Paypal' but they never clicked the 'Back to Merchant' tab so that we can process their userid and password.
This script shows ALL the people that are in the customer list and their associated place in the users database:
SELECT
`Transactions List`.`Customer Email`,
users.Email,
`Transactions List`.`Transaction ID`,
users.`Name`,
users.Phone
FROM
`Transactions List`
INNER JOIN users ON `Transactions List`.`Customer Email` = users.Email
What I'm seeking to do is show the INVERSE of that. i.e. all the people who LOST their way. They DO appear in the TRANSACTIONS LIST table but do NOT appear in the USERS table.
Anyone have an idea how to convert this MYSQL Query into the Inverse so we can quickly identify which customers did not get user accounts?
There's an existing post "Inner join inverse Php MySQL" here that wasn't answered that asks a similar question. Perhaps the person asking the question was not clear enough: Inner join inverse Php mysql
also
What is the difference between “INNER JOIN” and “OUTER JOIN”?
What is the difference between "INNER JOIN" and "OUTER JOIN"?
but neither of these methods actually do what I want the script to do.
What I'm seeking to do is show [...] all the people who [...] appear in the TRANSACTIONS LIST table but do NOT appear in the USERS table.
You could use not exists:
select t.*
from transactions_list t
where not exists (
select 1 from users u where t.customer_email = u.email
)
Another way to phrase this is to use an anti-left join (this is more in the spirit of your question, that relates to joins):
select t.*
from transactions_list t
left join users u on t.customer_email = u.email
where u.email is null
This means: try to join each transaction with a user, and filter on those that did not match.
select t.*
from `Transactions List` t
left join users u on t.`Customer Email` = u.email
where u.email is null
Given the above syntax and the name of the table in the database as specified above this is the correct answer. Thank you to GMB for answering the question. For other readers, keep in mind that if your database tables include spaces in their names or field names then you must use the scare quotes to identify your table or field names. This is commonly used when importing tables into MySQL from 3rd party tools.

SQL query that returns all the ids without a certain value

I have a table that maps User and Feature. Basically what features are enabled for each user. The table is |userId|featureId| with one(user) to many(feature) relationship.
I would like to create a query that takes a list of userIds and returns the list of userIds that are missing a specific feature.
Meaning I need to make sure that every id has a specific featureId.
userId featureId
1 A
1 B
2 A
3 C
4 D
3 A
So in this example, I'll get the list of ids (1, 2, 3, 4) and a featureId A and the query will return one row with userId 4 since it's the only userId with the feature A enabled.
To find a list of users that don't have feature X I would left join to the list of users that has that feature and return the ones not there. Like this:
SELECT *
FROM table_you_did_not_name as base
LEFT JOIN (
SELECT DISTINCT userID
FROM table_you_did_not_name
WHERE feature = 'X'
) as sub ON base.userID = sub.userID
WHERE sub.userID is null
I think I may have answered a different question: this doesn't address your data; but I'm unsure how you determine it is user 4 you want returned. as each user is missing some of the features the others have. Perhaps we just need to add a where clause below for the specific feature(A) in your example?
Think of data in terms of sets
You need
a set of data for all users (User or something)
a set of data for all features (feature)
and what features a user has (User_Feature)
Then you need to
Generate a set of every feature to every users (cross join)
Identify which of those the user has identified. (left join in user_feature)
and then only keep those where no feature has been identified (where no record in user_feature)
One method: This basically says return the features for each user that exist in a feature list, but have not been associated to a user.
SELECT U.userID, F.FeatureID as FeatureIDMissing
FROM USER U
CROSS JOIN FEATURE F
LEFT JOIN UserFeature UF
on U.UserID = UF.UserID
and F.FeatureID = UF.FeatureID
WHERE UF.UserID is null
-- and F.FeatureID = 'A' --maybe add this?
Alternate method: (combine two steps (2,3) by simply excluding those features which already exist for the user.
In english this says, return all the features for each user for which a user has not been associated
SELECT U.userID, F.FeatureID as FeatureIDMissing
FROM USER U
CROSS JOIN FEATURE F
WHERE not exists (SELECT *
FROM userFeature UF
WHERE U.UserID = UF.UserID
and F.FeatureID = UF.FeatureID)
--and F.FeatureID = 'A' --maybe add this?
Either answer should return the same results. It's a matter of preference database and performance .. Look at the execution plans to help decide which is best for you and your data.
Now maybe you mean you give a list of userID's you want to generate a unique set of features for all those users, and then return users w/o those features. If so instead of a cross join to feature you just need to use (Select distinct FeatureID from userFeatures where UserID IN ('yourListHere') this will generate a unique set of features for those users and identify which users are missing certain features shared with that set of users.
So...
SELECT U.userID, F.FeatureID as FeatureIDMissing
FROM USER U
CROSS JOIN (SELECT distinct FeatureID
FROM userFeatures
WHERE UserID IN ('yourListHere')F
LEFT JOIN UserFeature UF
on U.UserID = UF.UserID
and F.FeatureID = UF.FeatureID
WHERE UF.UserID is null
-- and F.FeatureID = 'A' --maybe add this?
as an example.

Can you build a MySQL query to show not found results from the conditional

I am trying to find out how to find the emails that do not exist in a table using the emails from the conditional.
I could create a table with these emails but that seems like overkill for what I need it for.
What I am looking for is a query that would show me the conditional value and NULL as the user ID.
Is this possible?
I have a query like this:
SELECT u.uid, u.mail
FROM `users` u
WHERE u.mail IN (
'alot#of',
'emails#that',
'ineed#tofind',
)
This works great at finding the emails and associating the user id. Now I need to identify which emails do not exist in the result. I am currently only using 56 emails and 6 do not appear in the list. I am trying to identify which emails are not found.
NOT IN won't work as I have over 40,000 users. I only want to identify the emails not found from my conditional. I have 56 emails and only 50 results. I need to identify the 6 not found (they may not even be in the table at all)
Let me attempt to clarify this a little more:
I am given a list of emails for supposed accounts in the system. I am trying to find the accounts from the given email. This part is fine. Now, the issue I am having, I was given 56 emails but only 50 were found. I need to identify which emails out of the 56 were not found. The emails are all thrown into the conditional. NOT IN won't work because it would return all user but the 50 that were found. (roughly 40,000) I just need to identify the emails from the conditional that were not found in the table.
Thanks for any insight or suggestions to do what I need.
There isn't a way to do what you want without creating some additional items to track the emails. Basically, you're trying to get MySQL to tell you which items in the WHERE portion aren't found, but MySQL can only tell you about rows in a table.
You need to make a secondary table that stores the email addresses from your list, call it list. I would make it a single column table with just the emails. Then LEFT JOIN it against the users table and find where the uid is null.
SELECT u.uid, l.mail
FROM `list` l
LEFT JOIN `users` u ON u.mail=l.mail
WHERE u.uid IS NULL
As posted in the comments, NOT IN may be helpful. But there are also other ways. One of them is to left join your table with the result of your query and show only non-coincident rows:
select u.uid, u.mail
from users as u
left join (
select u.uid, u.mail
from users
where mail in ('alot#of','emails#that','ineed#tofind')
) as a on u.uid = a.uid
where a.uid is null;
Add the fields you need to the join (if uid is not enough)
So your question now becomes more complicated... you want to find all the E-Mails in your condition that are not found in your table.
As far as I know, there's not a simple SQL sentence that will give you that... but you can work with temp tables and get it. The solution implies:
Create a temporary table to hold the values you want to search (and add the appropriate indexes to it)
Insert the values you want to search
Execute a select query to find non-matching rows
So... let's do it:
-- 1. Create a temp table to hold the values
drop table if exists temp_search_values;
create temporary table temp_search_values (
mail varchar(100),
unique index idx_mail(mail) -- Don't allow duplicate values here
);
-- 2. Insert the search values
insert into temp_search_values (mail) values
('alot#of'),('emails#that'),('ineed#tofind');
-- 3. Execute the query
select a.*
from users as u
left join temp_search_values as a on u.mail = a.mail
where u.mail is null;
Remember: Temporary tables are only visible to the connection that created them, and are deleted when the connection is closed or killed.
NULL is a strange result. It's not true and it's not false. If you want to check for it, you have to look specifically.
SELECT u.uid, u.mail
FROM `users` u
WHERE u.mail NOT IN (
'alot#of',
'emails#that',
'ineed#tofind',
) and u.uid IS NULL
* Oh, I see what you're getting at. This will work, although it's not pretty. *
select * from
(SELECT 'emails#that' as v
UNION SELECT 'alot#of' as v,
UNION SELECT 'ineed#tofind' as v
) as test
left join users on u.mail = test.v
where u.uid is null

MySQL query to join 3 tables while finding non-matches

(I could not come up with an concise title)
Hello All
I have been a long time viewer of the site, but after running into an issue I cannot resolve I thought I would sign up to ask a question. I am not sure how to phrase this issue which made searching for help on it difficult and it could very well be that this has been answered and I simply do not know the right search criteria to find the answer. It may also be that there is not an answer for what I am trying to do. Apologies if I have not used the correct procedure to ask.
What I am trying to achieve I can easily do via scripting, and it may be that's really the only appropriate way to do this. My circumstance puts me in a situation where I am being asked if I can do this via MySQL query only.
ISSUE:
Here is the nature of the situation (for which I don't know how to easily summarize into a topic). I have 3 tables in a mySQL database:
Table: account
column: number (unique id)
Table: user
column: number (unique id)
column: account (links the user to an account)
Table: service
column: number (unique id)
column: user (links the service to a user)
column: servdef (the type of service)
Basically an account holds multiple users and each user can hold multiple services.
I have been asked to write a query which will show all accounts (account.number) which do not contain services (service.number) of a particular type (service.servdef). I have seen posts on this site about using NOT EXISTS, but I have not been able to get it to work correctly for my situation. Is this even possible with a single SQL query? Thanks for any help!
Use LEFT JOIN for that since some users have no records on table service. Try,
SELECT a.*
FROM account a
LEFT JOIN `user` b
on a.number = b.account
LEFT JOIN service c
ON b.number = c.`user`
WHERE c.`user` IS NULL OR
c.servdef <> 'particularService'
SQLFiddle Demo
Using NOT EXISTS:
SELECT a.number
FROM account a
WHERE NOT EXISTS (
SELECT u.account
FROM user u INNER JOIN service s ON (u.number = s.user)
WHERE u.account = a.number
AND s.servdef = 'your_particular_type'
)
DEMO (sqlfiddle).
Here is an adaptation of an accepted answer given in another post I saw yesterday.
#servdef is the service type not existing for the accounts you want to find.
select a.number
from account a
inner join user u on a.number = u.account
inner join service s on u.number = s.user
group by a.number
having max(case when s.servdef = #servdef then 1 else 0 end) = 0
(Gordon if you read this, feel free to laugh)
NOT IN is generally the slowest form, but NOT EXISTS may be faster than LEFT JOIN depending on your engine and indexes. Try each one and see.
SELECT a.number
FROM account a
WHERE a.number NOT IN (
SELECT u.account
FROM user u
INNER JOIN service s ON u.number = s.user
WHERE s.servdef = 'your_particular_type'
)
Edit: Wrong reasoning for NOT IN, sorry. Although I find it slightly easier to read than NOT EXISTS it doesn't work well with nullable columns, although I don't think that applies here.
The problem turned out to be that the version of MySQL was too old to support sub queries. One the server was upgraded to new hardware and a modern operating system, the issue was resolved. The other answers are technically correct, but only work in modern versions of MySQL. It was unbeknownst to me at the time of the question that the version of MySQL was a factor. I was not something I even thought to check.

Mysql fetching data from multiple tables

I am having three tables
user(id,fname,lname)
picture(picid,pic_path,userid)
friends(friend_of,friends_id,status)
I want to fetch id,fname,lname,pic_path for all friends_id of friends_of=1
picture path may be null
Thankx in advance.
What you're looking for is called a join. In particular, you're looking for a JOIN and a LEFT JOIN.
SELECT
user.id, user.fname, user.lname, picture.pic_path
FROM
friends
JOIN user
ON user.id = friends_id
LEFT JOIN picture
ON userid = user.id
WHERE
friends.friend_of = 1
This will only work though if there's a maximum of 1 entry in picture.
Though this answers your immediate question, I suggest you become very familiar with joins. They're one of the fundamental operations of a relational database. (Note that a join essentially is a mathematical relation)
Try this
SELECT u.*,p.*,f.*
FROM USER u
LEFT JOIN picture p ON p.user_id = id
INNER JOIN friends f ON f.friends_of = u.id
WHERE f.friends_id = 1
For querys like that you need to understand and employ the relations between your entities. Then you work in two steps: selection and projection and contrary to what SQL-syntax may imply the part before the FROM keyword is the projection.
First we compile data. Clearly we need the data from all three tables and we need it once. So at first we construct all possible combinations, by joining all three tables. In SQL this is done in the FROM part, i.e.
FROM friends f, picture p, user u
f, p and u are aliases which serve the purpose of saving us the efford of typing the full table names in the following.
Now we have all possible combinations. Let's select the ones we want:
I suppose every picture in your DB belongs to a user which is stored in your DB as well. So my assumption is that you only want pictures associated to a user. Hence we have a first restrictions on all the combinations we made before! The restriction derived from the (probable) meaning of the data stored in you database and stated as:
u.id = p.userid
(Notice: by applying this restriction to combination above we "select" only certain combinations.)
Then you already stated another restrictions as a request "friends_of=1" to associate this constraint on the combination we write:
f.friends_of=1
Then we combine your request "friends_of=1" with the other data by:
f.friend_of = u.id
This constraint selects only those users who are a friend of somebody. Now we can combine these constraints. As we want all constraints to be satisfied we AND them in a WHERE statement:
WHERE u.id = p.userid AND f.friend_of = u.id AND f.friends_of=1
The ordering does not affect meaning (in this case. But let's rethink those constraints:
u.id = p.userid : we want information about the user and the pictures associated with that user
f.friend_of = u.id : we are looking for a users who are friends of somebody
f.friends_of=1 : we are looking for friends of a particular somebody
Now we project the data stored in our DB to what we want. We want all the user data and picture paths. In SQL:
SELECT u.*,p.pic_path
Now we put everything together.
SELECT u.*,p.pic_path FROM friends f, picture p, user u WHERE u.id = p.userid AND f.friend_of = u.id AND f.friends_of=1
To allow for friends that don't have a picture associated with them (note: that's very different to pic_path being NULL) you need an outer join, which means you also want combinations with empty sets. That's where my MySQL is not so good but I'd guess you'd generate all combinations you want (and many more) with:
FROM friends f JOIN user u LEFT JOIN picture p ON u.id = p.userid
and
SELECT u.*,p.pic_path FROM friends f JOIN user u LEFT JOIN picture p WHERE f.friend_of = u.id AND f.friends_of=1
Notice, that the constraint that may be violated has been made explicit by moving it from the general selection to the generation of the data combinations as a rule on how to create combinations. And yes, it's a shortcut instead of following through the idea of selection and projection.