Fetching datasets from crosslinked table in a single query - mysql

Imagine the following MySQL table environment:
admins
id username
usergroups
id groupName
adminXusergroup (crosslinking table)
id adminId groupId
adminId and groupID are indexed and foreign key relations are configured accordingly (adminId points to admins.id and groupId points to usergroups.id).
All id fields are the primary keys.
I would like to retrieve all datasets from usergroups that correspond to a specific user in admins.
I have managed to do this in PHP by first fetching the according rows from adminXusergroup and then creating a list of IDs to select from admins in a second call. But even though it works, i feel like it can be optimized further, possibly by fetching all required information in a single MySQL query (joins perhaps?).
How can i fetch all datasets from usergroups that correspond to a specific dataset in admins (based on the id) by incorporating the information in the crosslinking table adminXusergroup ?
My current approach started like this:
SELECT * FROM usergroups WHERE usergroups.id=(SELECT id FROM adminXusergroup WHERE adminId="1");
But it doesn't seem right, and i am not sure as to how to apply nested selects in such a case?

I think you can achieve this using IN condition
SELECT *
FROM usergroups
WHERE usergroups.id IN
(
SELECT id
FROM adminXusergroup
WHERE adminId=1
);
Otherwise you can use an INNER JOIN query
SELECT *
FROM usergroups a
INNER JOIN adminXusergroup b
ON a.id = b.groupId
WHERE b.adminId = 1

Try This
SELECT a.id,u.*,x.* FROM admin as a
INNER JOIN usergroups as u on a.id=u.id
INNER JOIN adminXusergroup as x ON x.id=u.id WHERE u.id='userid'

Related

I'm trying to let a user who has been assigned something be able to view it with PHP and MYSQL

I've tried searching an answer for this and I can't realy find the one that fits what I'm looking for.
I'm making a small web application that allows an admin to create course material and a test, that a user will be assigned.
What I am trying to figure out, is how a user, who has been assigned a course, can access that course by clicking a button.
I have created one table which stores the user information, with a user ID being a primary key. I have a course table, which stores all materials and the course's test, with a course id being that table's primary key.
I made a third table which is an assigned_courses table, it contains two foreign keys. One referencing the user id from the user table, and one referencing the course id on the course table.
I've inserted a few images to show what you mean.
I can't quite figure out the Sql syntax to pull course materials and test based on the assigned_course table.
All feedback appreciated. This is the two foreign keys that reference the user id (id) and the course id(u_id)
Not sure what column names you have so I used * instead. You can use JOIN (AKA INNER JOIN) to link the tables together. You join on the primary keys of the 2 tables.
I have created one table which stores the user information, with a
user ID being a primary key. I have a course table, which stores all
materials and the course's test, with a course id being that table's
primary key.
I used id for your user table and course_id for the course table. Change as needed.
Find all courses for a given user.
SELECT a.*
FROM course a
JOIN assigned_course b ON a.course_id = b.id
WHERE b.u_id = :userId
;
Find all users for a given course.
SELECT a.*
FROM user a
JOIN assigned_course b ON a.id = b.u_id
WHERE b.id = :courseId
;
EDIT #1 - Subquery without JOIN
Thanks for your feedback, as I stated above, I was hoping to avoid a
join.
I highly recommend you learn joins. They are essential when you need to query more than 1 table. In this case, you can use a sub-query to find matching courses for a user but you won't be able to access that user data at the same time; that is the power of a join. I'll include a final example underneath showing how to get courses and user data at the same time.
Find all courses for a given user (using sub-query)
SELECT a.*
FROM course a
WHERE course_id IN (
SELECT id
FROM assigned_course b
WHERE u_id = :userId)
;
Final example showing courses related to users (many-to-many)
SELECT * -- You now have access to a, b, and c data
FROM course a
JOIN assigned_course b ON a.course_id = b.id -- joined by PKs
JOIN user c ON c.id = b.u_id -- joined by PKs
WHERE c.id = :userId
;
It sounds to me like you want to use SQL Join syntax which goes as follows:
select column1, column2, column3 etc...
from table1
join course on table1.id = course.id
join assigned_course on table1.id = assigned_course.id
replace the column1,2,3 etc with the actual column names, and replace the foreign_key_name with the actual name of the foreign key in the sub-table and the same for the user_primary_key
you can find some other examples of mysql joins here
as far as how to implement them into your html or php file, that is a very different question and would require knowing alot more about how your code is setup in the form
a place that is pretty user friendly is that might help with pulling the data is here just make sure you are aware of using prepared statements for your code

How to deal with bad data in mysql?

I have three tables that I want to combine.
I have the following query to run:
DROP TABLE
IF EXISTS testgiver.smart_curmonth_downs;
CREATE TABLE testgiver.smart_curmonth_downs
SELECT
ldap_karen.uid,
ldap_karen.supemail,
ldap_karen.regionname,
smart_curmonth_downs_raw.username,
smart_curmonth_downs_raw.email,
smart_curmonth_downs_raw.publisher,
smart_curmonth_downs_raw.itemtitle,
smart_items.`Owner`
FROM
smart_curmonth_downs_raw
INNER JOIN ldap_karen ON smart_curmonth_downs_raw.username = ldap_karen.uid
INNER JOIN smart_items ON smart_curmonth_downs_raw.itemtitle = smart_items.Title
I want to know how to create the joins while maintaining a one to one relationship at all times with rows in table smart_curmonth_downs_raw.
For instance if there is not a uid in ldap_karen I have issues. And then the last issue I have found is that our CMS is allowing for duplicate itemtitle. So if I run my query I am getting a lot more rows because it is creating a row for each itemtitle. For example would there be a way to only catch the last itemtitle that is in smart_items. I would just really like to maintain the same number of rows - and I have no control over the integrity issues of the other tables.
The smart_curmonth_downs_raw table is the raw download information (download stats), the karen table adds unique user information, and the smart_items table adds unique items (download) info. They are all important. If a user made a download but is knocked off the karen table I would like to see NULLs for the user info and if there is more than one item in smart_items that has the same name then I would like to see just the item with the highest ID.
It sounds like relationship between smart_curmonth_downs_raw and ldap_karen is optional, which means you want to use a LEFT JOIN which all the rows in the first table, and, if the right table does not exists, use NULL as the right table's column values.
In terms of the last item in the smart_items table, you could use this query.
SELECT title, MAX(id) AS max_id
FROM smart_items
GROUP BY title;
Combining that query with the other logic, try this query as a solution.
SELECT COALESCE(ldap_karen.uid, 'Unknown') AS uid,
COALESCE(ldap_karen.supemail, 'Unknown') AS supemail,
COALESCE(ldap_karen.regionname, 'Unknown') AS regionname,
smart_curmonth_downs_raw.username,
smart_curmonth_downs_raw.email,
smart_curmonth_downs_raw.publisher,
smart_curmonth_downs_raw.itemtitle,
smart_items.`Owner`
FROM smart_curmonth_downs_raw
INNER JOIN (SELECT title, MAX(id) AS max_id
FROM smart_items
GROUP BY title) AS most_recent
ON smart_curmonth_downs_raw.itemtitle = most_recent.Title;
INNER JOIN smart_items
ON most_recent.max_id = smart_items.id
LEFT JOIN ldap_karen
ON smart_curmonth_downs_raw.username = ldap_karen.uid;

MySQL selecting tables where user exists

Let's say I have a mySQL table called "user" that contains a userid and a password or something. Then I have other tables for different purposes, such as a table for their favorite food, which would contain their userid and favorite food.
Is there a way to query for tables that contains a specified userid and checks whether they have an entry in that table?
So if I have three tables called favorite_food, favorite_drink, and favorite_candy, but userid only had values in favorite_food and favorite_drink, I want the query to return favorite_food and favorite_drink.
I'm a beginner and I couldn't quite grasp the concept of linking. Userid is the primary key of user and the other tables reference it?
Read more about UNION. I think that this is what you need.
SELECT * from favorite_food where `userid` = 50
UNION
SELECT * from favorite_drink where `userid` = 50
UNION
SELECT * from favorite_candy where `userid` = 50
In SQL "linking" is represented by JOINs. Since (as you indicated) some users will not have 1 or 2 favorite objects we will need to use LEFT OUTER JOINs. Then we will have NULLs in the output in place of missing favorites.
In your case:
SELECT u.userid, f.food_name, d.drink_name, c.candy_name
FROM user u
LEFT JOIN favorite_food f ON u.userid=f.userid
LEFT JOIN favorite_drink d ON u.userid=d.userid
LEFT JOIN favorite_candy c ON u.userid=c.userid
ORDER BY u.user.id
This will give you favorites for all users in your user table.

Normalise data into one table

I'm trying to insert rows into a table (usersteps) from the table steps for all users only if the step id does not exist.
INSERT INTO userssteps
(status,user_id,step_id)
SELECT
'0' ,
(SELECT DISTINCT id from users),
(SELECT DISTINCT id from steps)
I get the following error on the above MYSQL
#1242 - Subquery returns more than 1 row
Reason:
A new user signs up they should get all steps, if I create a new step i'd want to create it in usersteps for current users to see.
If there is a more clever way to do this i'd love to know but i'm stumped. I am also using cakePHP so if there is a special cakePHP way to help me in this i'd prefer that.
Table Structure
steps:
id
name
users:
id
username
password
userssteps:
id
user_id
step_id
status
It looks like you are trying to produce a cartesian product. http://en.wikipedia.org/wiki/Cartesian_product.
If there is no relations between the users and steps table then they cannot be joined, only multiplied.
INSERT INTO userssteps
(status,user_id,step_id)
select 0,
users.id,
steps.id
from users
inner join steps
The subquerys (SELECT DISTINCT id from users) and (SELECT DISTINCT id from steps) will return ALL the id's. In a insert clause you will need only one value (you can't have more than 1 value).
you can try to inner join the two tables by the ID
Try this way:
INSERT INTO userssteps
(status,user_id,step_id)
select 0 as status,
users.id,steps.id
from users
inner join steps
on (users.id=steps.user_id);
That way should works ;)
PS: Now the join is right.
Saludos.

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 = ?