Mysqli - AND OR - mysql

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)

Related

Check values exist and not exist between 2 tables

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.

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

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 JOIN tables with WHERE clause

I need to gather posts from two mysql tables that have different columns and provide a WHERE clause to each set of tables. I appreciate the help, thanks in advance.
This is what I have tried...
SELECT
blabbing.id,
blabbing.mem_id,
blabbing.the_blab,
blabbing.blab_date,
blabbing.blab_type,
blabbing.device,
blabbing.fromid,
team_blabbing.team_id
FROM
blabbing
LEFT OUTER JOIN
team_blabbing
ON team_blabbing.id = blabbing.id
WHERE
team_id IN ($team_array) ||
mem_id='$id' ||
fromid='$logOptions_id'
ORDER BY
blab_date DESC
LIMIT 20
I know that this is messy, but i'll admit, I am no mysql veteran. I'm a beginner at best... Any suggestions?
You could put the where-clauses in subqueries:
select
*
from
(select * from ... where ...) as alias1 -- this is a subquery
left outer join
(select * from ... where ...) as alias2 -- this is also a subquery
on
....
order by
....
Note that you can't use subqueries like this in a view definition.
You could also combine the where-clauses, as in your example. Use table aliases to distinguish between columns of different tables (it's a good idea to use aliases even when you don't have to, just because it makes things easier to read). Example:
select
*
from
<table> as alias1
left outer join
<othertable> as alias2
on
....
where
alias1.id = ... and alias2.id = ... -- aliases distinguish between ids!!
order by
....
Two suggestions for you since a relative newbie in SQL. Use "aliases" for your tables to help reduce SuperLongTableNameReferencesForColumns, and always qualify the column names in a query. It can help your life go easier, and anyone AFTER you to better know which columns come from what table, especially if same column name in different tables. Prevents ambiguity in the query. Your left join, I think, from the sample, may be ambigous, but confirm the join of B.ID to TB.ID? Typically a "Team_ID" would appear once in a teams table, and each blabbing entry could have the "Team_ID" that such posting was from, in addition to its OWN "ID" for the blabbing table's unique key indicator.
SELECT
B.id,
B.mem_id,
B.the_blab,
B.blab_date,
B.blab_type,
B.device,
B.fromid,
TB.team_id
FROM
blabbing B
LEFT JOIN team_blabbing TB
ON B.ID = TB.ID
WHERE
TB.Team_ID IN ( you can't do a direct $team_array here )
OR B.mem_id = SomeParameter
OR b.FromID = AnotherParameter
ORDER BY
B.blab_date DESC
LIMIT 20
Where you were trying the $team_array, you would have to build out the full list as expected, such as
TB.Team_ID IN ( 1, 4, 18, 23, 58 )
Also, not logical "||" or, but SQL "OR"
EDIT -- per your comment
This could be done in a variety of ways, such as dynamic SQL building and executing, calling multiple times, once for each ID and merging the results, or additionally, by doing a join to yet another temp table that gets cleaned out say... daily.
If you have another table such as "TeamJoins", and it has say... 3 columns: a date, a sessionid and team_id, you could daily purge anything from a day old of queries, and/or keep clearing each time a new query by the same session ID (as it appears coming from PHP). Have two indexes, one on the date (to simplify any daily purging), and second on (sessionID, team_id) for the join.
Then, loop through to do inserts into the "TempJoins" table with the simple elements identified.
THEN, instead of a hard-coded list IN, you could change that part to
...
FROM
blabbing B
LEFT JOIN team_blabbing TB
ON B.ID = TB.ID
LEFT JOIN TeamJoins TJ
on TB.Team_ID = TJ.Team_ID
WHERE
TB.Team_ID IN NOT NULL
OR B.mem_id ... rest of query
What I ended up doing is;
I added an extra column to my blabbing table called team_id and set it to null as well as another field in my team_blabbing table called mem_id
Then I changed the insert script to also insert a value to the mem_id in team_blabbing.
After doing this I did a simple UNION ALL in the query:
SELECT
*
FROM
blabbing
WHERE
mem_id='$id' OR
fromid='$logOptions_id'
UNION ALL
SELECT
*
FROM
team_blabbing
WHERE
team_id
IN
($team_array)
ORDER BY
blab_date DESC
LIMIT 20
I am open to any thought on what I did. Try not to be too harsh though:) Thanks again for all the info.

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...