Check values exist and not exist between 2 tables - mysql

I have 3 tables, one of accounts, one of friends and another of consumers.
Something like this:
table_accounts
id | account_name
table_friends
id | account_id | people_id
table_consumers
id | account_id | people_id
I need to cross the following information:
Which consumer_id coexist in both tables, something simple like this:
SELECT
*
FROM
table_friends,
table_consumers
WHERE
table_friend.account_id = 12345
AND table_friend.account_id = table_consumers.account_id
GROUP BY table_friend.people_id
this query is very slow
Well, I now need to get what are the consumer_id's friends table, which are NOT in the consumers table. And in a third moment, find out which consumer_id does NOT exist in the friends table. But I think it's the same thing ...
My doubt is about logic, I can not think how to cross this information.

This is probably more or less what you try to do : (and take a look at Subqueries with EXISTS vs IN - MySQL )
SELECT *
FROM table_friends
WHERE
NOT EXISTS (
SELECT *
FROM table_consumers
WHERE table_consumers.people_id = table_friends.people_id
)
BTW, you say "this query is very slow" how many row you query ? what is "slow" ? do you have some index where you need them ?

Could you do something like this:
Select a.account_name
, a.id
, case when f.id is null then 0 else 1 end isFriend
, case when c.id is null then 0 else 1 end isConsumer
from table_accounts a
left join table_friends f on a.id = f.account_id
left join table_consumers c on a.id = c.account_id

If I understand your question correctly you can use NOT IN to find the exceptions for each table. Something like this:
SELECT id
FROM table_consumers
WHERE account_id
NOT IN
(SELECT account_id
FROM table_friends)
You can do the same thing with the table names reversed to find out which friends are not in consumers. If you were wanting to include more than one table in the query, you may want to check out using UNION or UNION ALL as well. See: UNION ALL and NOT IN together

Looks like you already got the answer on how to compose your query, but you should think about the redesign of your schema. If it's not too late.
Both table_friends, and table_consumers represent people. The only difference is what type/kind of people. You don't want to add a new table every time you need to add a new attribute to people.
What you need is:
table_accounts
table_people
table_people_type
table_people_type_mapping
The last one being a mapping table between table_people and table_people_type.
In table_people_type you could have friends and consumers for now, but you could also add different types later on without schema change. And your queries would be more intuitive.
Again, that is in case if schema change is still an option for you.

Related

How can I filter out results based on another table. (A reverse join I guess?)

Basically, I have a table which contains two fields: [id, other] which have user tokens stored in them. The goal of my query is to select a random user that has not been selected before. Once the user is selected it is stored in the table shown above. So if Jack selects Jim randomly, Jack cannot select Jim again, and on the flip side, Jim cannot select Jack.
Something like this is what comes to mind:
SELECT * FROM users
WHERE (SELECT * FROM selected WHERE (id=? AND other=?) OR (id=? AND other=?));
Well, first of all I've read that uses sub-queries like this is extremely inneficient, and I'm not even sure if I used the correct syntax, the problem is however, that I have numerous tables in my scenario which I need to filter by, so it would look more like this.
SELECT * FROM users u
WHERE (SELECT * FROM selected WHERE (id=? AND other=?) OR (id=? AND other=?))
AND (SELECT * FROM other_table WHERE (id=? AND other=?) OR (id=? AND other=?))
AND (SELECT * FROM diff_table WHERE (id=? AND value=?))
AND u.type = 'BASIC'
LIMIT = 1
I feel like there's a much, much more efficient way of handling this.
Please note: I don't want a row returned at all if the users id is present in any of the nested queries. Returning "null" is not sufficient. The reason I have the OR clause is because the user's id can be stored in either the id or the other field, so we need to check both.
I am using Postgre 9.5.3, but I added the MySQL tag as the code is mostly backwards comptable, Fancy Postgre only solutions are accepted(if any)
You can left join to another table, which produces nulls where no record is found:
Select u.* from users u
left selected s on s.id = u.id or s.other = u.other
where s.id is null
The or in a join is different, but should work. Example is kinda silly...but as long as you understand the logic. Left join first table to second table, where second table column is not null means there was atleast one record found that matched the join conditions. Where second table column is null means no record was found.
And you are right...avoid the where field = (select statement) logic when you can, poor performer there.
Use an outer join filtered on missed joins:
SELECT * FROM users u
LEFT JOIN selected s on u.id in (s.id, s.other) and ? in (s.id, s.other)
WHERE u.id != ?
AND s.id IN NULL
LIMIT 1

MYSQL - QUERY FROM TWO TABLES

Question - let's say I have 2 tables.
Table 1 - name is permission_list, columns are ID (unique ID), col_ID, user_ID
Table 2 - name is list_entries, Columns are ID (unique ID), title, description, status
I want to select all the rows from table 2 that have status of 'public' as well as all the rows from table 2 that the ID from table 2 shows up in table 1 (under the column col_ID) AND if the user_ID in table 1 matches a certain value. So, anything public, or anything that this specific user has listed under the permissions table. This query would also remove duplicates - in case the user gets a public entry listed in their permissions_list, it wouldn't show up twice.
Hope that makes sense!
Here you go:
SELECT DISTINCT table2.* from table2
LEFT JOIN table1 USING (id)
WHERE status='public'
OR user_ID='someuser';
You need to get some education on JOIN for your first thing, and the second thing is called DISTINCT.
Start here... https://www.google.com/
You have not specified your join condition so we can't give you code samples really. Also the way you worded your question, I'm not entirely sure you don't want a UNION. Read up on those concepts and come back here when you can improve the question.
SELECT table_2.status, table_2.ID, table_1.col_ID
FROM table_1 JOIN table_2
WHERE table_2.status = 'public'
AND table_2.ID = table_1.col_ID
AND table_1.user_ID = 'certain value'
;
Try this

Mysqli - AND OR

I have been trying to make my database show my contacts. But other users can give people access to their contacts (this is all stored in tblaccess)
I tried using this sql
SELECT *
FROM tblcontacts, tblaccess
WHERE (
tblaccess.user =1
AND tblaccess.contact = contactID
)
OR tblcontacts.userid =1
But it would only return a result if tblaccess has a row in.
The conditions are that if tblcontacts.userid <> 1
then check if tblaccess.user = 1
Thanks in advance
Here is the way that I'm understanding your question. You want all contacts where userid = 1, unless there is a record in tblaccess. If there is such a record, then the user column must be 1.
The following query implements this:
SELECT *
FROM tblcontacts c left outer join
tblaccess a
on a.contact = c.contactID
where c.userid = 1 and
(a.concatId is NULL or
a.user = 1
);
You shouldn't need to have too many conditionals - this is what JOINs are for. I believe that a RIGHT JOIN is what you're looking for. This is untested, but should return a NULL value on the right side if tablecontacts returns a userid 1 but tablecontacts does not have a userid.
SELECT *
FROM tblcontacts
RIGHT JOIN tblaccess ON tblaccess.user=tblcontacts.userid
WHERE tablecontacts.userid=1
UPDATE
Sounds like you are looking for two separate pieces of data from two separate tables. I know they are both IDs, but there is no foreign key to connect to in either table. In fact, the data in one table may well not be in another table. For this reason you will probably want to run a UNION query. That will allow you to pull anybody in either table that holds these values.
SELECT * FROM tblaccess WHERE user=1 AND contact = contactID
UNION
SELECT * FROM tblcontact WHERE userid=1
Alternatively, you could create a prepared statement or put the logic into your programming language.
A quick note - I would recommend explicitly calling your columns. Most people far more experienced than I would be hammering that home so figured I'd politely suggest it.
Try this:
SELECT *
FROM tblcontacts
WHERE tblcontacts.userid = 1
AND EXISTS (SELECT 1
FROM tblaccess
WHERE tblaccess.user = 1)

Need to find all client id's the most efficient/fastest way using mySql

I have a bridging table that looks like this
clients_user_groups
id = int
client_id = int
group_id = int
I need to find all client_id's of of clients that belong to the same group as client_id 46l
I can achieve it doing a query as below which produces the correct results
SELECT client_id FROM clients_user_groups WHERE group_id = (SELECT group_id FROM clients_user_groups WHERE client_id = 46);
Basically what I need to find out is if there's a way achieving the same results without using 2 queries or a faster way, or is the method above the best solution
You're using a WHERE-clause subquery which, in MySQL, ends up being reevaluated for every single row in your table. Use a JOIN instead:
SELECT a.client_id
FROM clients_user_groups a
JOIN clients_user_groups b ON b.client_id = 46
AND a.group_id = b.group_id
Since you plan on facilitating clients having more than one group in the future, you might want to add DISTINCT to the SELECT so that multiple of the same client_ids aren't returned when you do switch (as a result of the client being in more than one of client_id 46's groups).
If you haven't done so already, create the following composite index on:
(client_id, group_id)
With client_id at the first position in the index since it most likely offers the best initial selectivity. Also, if you've got a substantial amount of rows in your table, ensure that the index is being utilized with EXPLAIN.
you can try with a self join also
SELECT a.client_id
FROM clients_user_groups a
LEFT JOIN clients_user_groups b on b.client_id=46
Where b.group_id=a.group_id
set #groupID = (SELECT group_id FROM clients_user_groups WHERE client_id = 46);
SELECT client_id FROM clients_user_groups WHERE group_id = #groupID;
You will have a query which gets the group ID and you store it into a variable. After this you select the client_id values where the group_id matches the value stored in your variable. You can speed up this query even more if you define an index for clients_user_groups.group_id.
Note1: I didn't test my code, hopefully there are no typos, but you've got the idea I think.
Note2: This should be done in a single request, because DB requests are very expensive if we look at the needed time.
Based on your comment that each client can only belong to one group, I would suggest a schema change to place the group_id relation into the client table as a field. Typically, one would use the sort of JOIN table you have described to express many-to-many relationships within a relational database (i.e. clients could belong to many groups and groups could have many clients).
In such a scenario, the query would be made without the need for a sub-select like this:
SELECT c.client_id
FROM clients as c
INNER JOIN clients as c2 ON c.group_id = c2.group_id
WHERE c2.client_id = ?

MYSQL : Problem in writing where clause according to scenario

I have a DB (user_interests) set up with 3 fields: i_id (unique), interest_id, uid.
Then a second DB (interests) set up with the interests: interest_id (unique), interest_name
I'd like to do an SQL query to return a list of interests that two users have in common: User A (owner of a profile) and user B (you/viewer of a profile). I guess I need to query user_interests, then JOIN interests to get the name of the interest.
SELECT user_interests.i_id, user_interests.uid, interests.interest_name
FROM databases.user_interests
LEFT JOIN databases.interests
ON interest.interest_id = user_interest.interest_id
WHERE _______________
I'm confused about the where clause (if that is the correct way to do it at all). My goal is to get the interest_id from user_interests.interests where user_interests.uid is both A and then B (in separate rows).
I saw this link, but couldn't figure out what exactly I was missing: Group by x where y = A and B and C
I would solve it by joining two copies of user_interests, one which is filtered for user A (the profile owner), and one for user B, (the profile viewer).
SELECT *
FROM interests I
INNER JOIN user_interests A ON
A.interest_id = I.interest_id
AND A.user_id = {profile owner}
INNER JOIN user_interests B ON
B.interest_id = I.interest_id
AND B.user_id = {profile viewer}
Alternatively, more along the lines of the snippet you provided, you could complete the where clause with something like...
SELECT * FROM interests
WHERE interest_id in (SELECT interest_id
FROM users
WHERE user_id = A)
AND
interest_id in (SELECT interest_id
FROM user_interests
WHERE user_id = B)
Hope one of those works for you! Let me know if I can clarify
I don't think you need the where clause in this case just remove it and you will get the set of data you are looking for:
SELECT user_interests.i_id, user_interests.uid, interests.interest_name
FROM databases.user_interests
LEFT JOIN databases.interests
ON interest.interest_id = user_interest.interest_id
You may also create a where statement such as the following if you are looking to get a specific result set. I'm not discrediting the answer previously submitted, I am simply trying to help you with the specific WHERE statement you're looking for.
SELECT user_interests.i_id, user_interests.uid, interests.interest_name
FROM databases.user_interests
LEFT JOIN databases.interests
ON interests.interest_id = user_interests.interest_id
WHERE user_interests.uid IN ('A','B');
Please also note, that I changed your ON join to use interests and user_interests, with 's' appended to both, as those are the names of the table. They maintain the same schema name as they are assigned in the database.
Your query is correct remove the where part and run it. It will give you the same output as you need...