Show a group project to multiple users mysql - php

I have two tables the group_table where all the groups information is stored.
And i have a group_person_table where all id's which correlate to groups
I only want to display the group information to multiple users if they match in group_person_table
the database looks like this :
Group_table
person_id, group_id, groupname, groupdesc.
Group_person_table
person_group_id, group_id, person_id
currently i only have the following query:
"SELECT * FROM group_table WHERE person_id = $person_id" IN (SELECT person_id FROM group_person_table WHERE person_id =$person_id);
I currently am able to display all of the groups to all of the users but i only want to display the groups to the users if they're the same in group_person_table so user A and B are in group A so they both see Group A, if user A is in Group B, then User B wont see group B!
I'm displaying calling upon the data in OOP
public function get_project($person_id){
//$sql3="SELECT * FROM group_table WHERE person_id = $person_id ";
$sql3="SELECT * FROM group_table WHERE person_id = $person_id";
$results = mysqli_query($this->db, $sql3);

One very important thing when creating a relation between tables is to decide where to keep the foreign key that will be used for linking them. Having a foreign key to the persons table in the group table, would restrict the group to one person only, which you obviously don't want. So, you can remove that column from the group_table and use only the group_person_table for the relations between the users and groups.
After adjusting this, your best choice would be an inner join, because nested select statements can really slow things down when working with larger data sets. And you want to get used to using the better approach :).
Your query would then be:
"SELECT * FROM group_table
JOIN group_person_table ON group_table.group_id = group_person_table.group_id
WHERE group_person_table.person_id = {$person_id}"

Related

Get only unique entries from pivot table in Laravel

I have two tables namely users and activities which has many to many relationships in my Laravel app. So, I have a pivot table activity_users with user_id and activity_id. I know that I can simply fetch all the users enrolled in an activity with a many to many relationship in my model.
But I want to get only those users from the pivot table which are enrolled in only one activity. I have been trying to do this for many days and I am still stuck. I have created a work around to filter the values based on activities_count after fetching from the database, but I wanted a more efficient solution.
Table users
id
name
1
User A
2
User B
Table activities
id
name
1
Activity 1
2
Activity 2
Table activity_users
activity_id
user_id
1
1
1
2
2
1
From this sample data what I want is: if I query for unique users in Activity 1, it should return only User B (user_id = 2) as he/she is not enrolled in other activities. And if I query for unique users in Activity 2, it should return null because it has only one user i.e. User A and he/she is already enrolled in Activity 1.
Fetching distinct values doesn't work as it just removes duplicates. I have already set to not allow duplicate combination of values in pivot table.
How about use Having?
You can find user with only one Activity like:
SELECT user_id FROM activity_users
GROUP BY user_id
HAVING count(user_id) = 1
Like Honk der Hase said in comments
Providing some code would help, if only to show more information on your relationships between models. But at a guess I think something like this should work:
$users = User::with("activities")
->withCount("activities")
->having("activities_count", 1)
->whereHas("activities", fn ($q) => $q->where("activities.id", $activity_id));
We eager load the activities relationship and get the count as an alias, so we can catch only those with a single activity. Then we filter the relationship to search for the provided activity id.
This assumes you've set up your User and Activity models with a proper many-to-many relationship.
For completeness, the resulting typically (for Laravel) verbose SQL is:
SELECT `users`.*, (
SELECT COUNT(*)
FROM `activities`
INNER JOIN `activity_user` ON `activities`.`id` = `activity_user`.`activity_id`
WHERE `users`.`id` = `activity_user`.`user_id`
) AS `activities_count`
FROM `users` WHERE EXISTS (
SELECT * from `activities`
INNER JOIN `activity_user` ON `activities`.`id` = `activity_user`.`activity_id`
WHERE `users`.`id` = `activity_user`.`user_id`
AND `activities`.`id` = ?
)
HAVING `activities_count` = ?

SQL - Return books that user owns

I'm doing I'm having a bit of a problem performing a query in my university project. I got a website where users share the books that they've read and I have a page where the user can view the books he has added (the books he owns).
For that I believe I need the logged user's id, which I store in a session PHP variable, the user id is in table users.
The information about the books is stored in a table books and it has its own id primary key.
Then, to show who owns what I have a table owns_book (id, u_id, book_id).
Right now for testing I've got 26 books total, 25 of them are added by a user with id of 57 and 1 book by user with id of 49.
When I run this query:
SELECT id, title, author, category, cover, added, user_id FROM books, users
WHERE user_id=49 AND id IN(SELECT book_id FROM owns_book)
AND user_id IN(SELECT u_id FROM owns_book)
And the result is a mess, I don't get the expected one book, I also get books added by the other user.
Can someone please show me the correct query that I need or if I need to change the structure of my tables? Thanks.
EDIT:
users(user_id, ...)
books(id, title, author, publisher, published, cover... )
owns_book(id, u_id, book_id)
It looks like you're looking to to grab everything from your books table that is owned by a specific customer. If that's the case, you can try
SELECT * FROM books
JOIN owns_book
ON books.id = owns_books.book_id
WHERE owns_book.user_id = 49
This will select all of the props from your books table then joins the tables based on on the ID of the book being equal to the book_id of the owns_book. Lastly, add the parameter - you only want to see user_id = 49.
You can simplify this query and use a LEFT JOIN...
SELECT books.id, title, author, category, cover, added, users.user_id
FROM users
LEFT JOIN owns_book on owns_book.user_id = users.user_id
LEFT JOIN books on books.id = owns_book.id
WHERE users.user_id=49
This links the user_id and lists any books owned by this user_id ( the ON bit of the JOIN). The WHERE clause just limits to listing records for the user_id your after.
If in the main list of columns, there is a column on multiple tables ( like user_id) then prefix it with the table name to allow the database to detect which column you want to use (even though they may be the same value).
You could also use inner join to join the tables users and books with the owns_book table:
SELECT id, title, author, category, cover, added, user_id
FROM owns_book
INNER JOIN users ON users.id = owns_book.u_id
INNER JOIN books ON books.id = owns_book.book_id
WHERE users.user_id=49

Select all from table where another query retuns no results

I'm creating a register web application for someone and I want to make student search to add a student to the activities register slightly more accurate.
At the moment my prepared statement is:
("SELECT * FROM students WHERE first_name LIKE :pattern OR last_name LIKE :pattern OR id LIKE :pattern ORDER BY last_name ASC");
And that works great. I returns a list of all students that match the search query. However after doing that query, for each row that returns I want to check if the students.id and $_GET['activity'] do not appear in the participants table already.
$_GET['activity'] is an ID from activities.id
The final result I want is to display all students that are not already registered on that activity.
Is this possible in one query? As if it is I'd rather do that then have to run a query on each returns result to see whether it should be displayed or not.
I have looked into INNER JOIN as I've used it before, but I don't feel that is what I need. My main issue is how to run that query to check if each result is in the participants table already.
Hopefully that will make sense as I'm finding it hard to work out how to word it in my head.
My table structure:
students - id PRIMARY KEY AI, first_name (varchar255), last_name (varchar255), dob (date)
activities - id PRIMARY KEY AI, title (varchar255), description (varchar255)
participants - id PRIMARY KEY AI, student_id (INT), activity_id (INT)
EDIT Updating this to use the three tables in the question
If you want all students who do NOT have a certain activity, you use a query pattern like this. The LEFT JOIN retains all the records from the students table, and places NULL values in the columns from activities where the ON condition fails to match. You then throw in a WHERE condition to keep only those NULL values. Like so:
SELECT s.id, s.first_name, s.last_name
FROM students s
LEFT JOIN participants p ON s.id = p.student_id
LEFT JOIN activities a ON p.activity_id = a.activity_id AND a.activity LIKE :act
WHERE a.activity_id IS NULL
AND ( s.first_name LIKE :a OR s.last_name LIKE :b OR etc etc )
If your input is an activity_id, it's even easier.
SELECT s.id, s.first_name, s.last_name
FROM students s
LEFT JOIN participants p ON s.id = p.student_id AND p.activity_id = :act
WHERE p.activity_id IS NULL
AND ( s.first_name LIKE :a OR s.last_name LIKE :b OR etc etc )
As you've noticed, INNER JOIN can't do this, because it leaves out the rows from the first table that don't match the ON condition. But those rows are the very ones you want, hence the LEFT JOIN ... WHERE ... IS NULL.
Beware some things:
Don't use LIKE to match id values.
Don't use SELECT *. Instead, name the columns you want in your result set.
The sequence of OR ... LIKE clauses in your filter for the student table is not going to perform very well.
You could use a subquery to get the studentids that are registered for $_GET['activity'], and then remove them from the returned rows using NOT IN in your WHERE condition.
SELECT * FROM students
WHERE
(first_name LIKE :pattern OR last_name LIKE :pattern OR id LIKE :pattern)
AND
id NOT IN
(SELECT student_id FROM participants WHERE activity_id = :activity)
ORDER BY last_name ASC
where :activity is the placeholder for $_GET['activity']
try this query
"SELECT * FROM students WHERE first_name LIKE :pattern OR last_name LIKE :pattern OR id LIKE :pattern AND id NOT IN (select student_id From participants WHERE activity_id = activity)
Here 'activity' is $_GET['activity'] .

PHP/MySQL Many-to-many relationship - the next step here

I'm new to advanced MySQL having only come across many-to-many relationships yesterday. I'm working on a project where users can join multiple projects and projects can accommodate multiple users.
My tables are:
Users - ID, name, email, password etc
Projects - ID, name, URL etc
Following advice from this site, I have set up a linking table with two foreign keys matched to the IDs of the above tables:
Users_Projects - Users_ID, Projects_ID
I understand the next step is something to do with joining, but how do I add a user to a table, or see who the members of a project are/what projects a particular user is a member of?
Projects for a given user:
SELECT *
FROM Projects p
LEFT JOIN users_projects up ON p.projects_id = up.projects_id
WHERE up.users_id = [INSERT USERID HERE]
To assign user to project you need to type user id and project id into the User_Projects table, to see which users are in selected project you can type:
SELECT Users.name, Users.email etc.. from Users_Projects JOIN Users on Users.ID =
Users_Projects.Users_ID JOIN Projects ON Projects.ID = Users_Projects.Projects_ID WHERE (Put your where statement here)
Read more on wiki: JOIN wikipedia
I think you forgot an id (in users, create new fields 'projectid' on user table)
ADD USER => INSERT INTO users (name,email,...) VALUES ('fred','redeyes#XX.com',...);
CHECK USER => SELECT name, email FROM USERS;
CHECK USER BY PROJECT ID => SELECT name, email FROM USERS, PROJECTS WHERE USERS.projectid = PROJECTS.id

How to call specific rows from a table in MySQL ? (see the case)

The website is intended for users to enable them joining the courses and posting their updates to these courses and also receiving the course updates made by others (imagen courses as Groups in Facebook but in my website users post updates ONLY through the courses)
When the user login to the website, he is suposed to see all the updates in the courses he already is joined in.
I have many tables in MySQL :
'updates' which have these attributes (id, account_id,course_id,datetime,content) noticing that course and account ID's are foreign keys..
'courses' which have these attributes (id,name,..)
'accounts' which have these attributes (id,full_name,...)
'accounts_courses_relationship' (account_id,course_id) , to map the relations between users and courses.
I have tried many times but the only thing I get is to show all the updates for all courses without excluding the updates from the courses that user isn't a member of, as follow:
$sql = "SELECT DISTINCT datetime, content
FROM updates
WHERE account_id != {$account_id}
ORDER BY datetime DESC
LIMIT 10";
So, How to exclude the updates from these courses?
Note: don't forget that all mappings between users and courses are registered in a table shown above..
Well i might be not seeing your problem it but how about
SELECT ... FROM updates where account_id = {$account_id}
Would't this select all updates for the current user and therefore all the updates the user is interested in?
something like this should work
select *
from updates u
join accounts_courses_relationship r on u.courseid = r.courseid
where r.account_id = {$account_id}
You have to inner join the updates and courses and accounts and accounts_courses tables.
select acr.accountid, acr.courseid, courses.name, accounts.fullname, updates.datetime, updates.content
from accounts_courses_relationship acr
inner join courses on acr.courseid=courses.courseid
inner join accounts on acr.accountid = accounts.accountid
inner join updates on updates.accountid= accounts.accountid and updates.courseid=courses.courseid
where accountid = {?}
order by updates.datetime desc
If you're interested in seeing updates to courses from other users (not you) and you're a registered user of those courses:
SELECT DISTINCT datetime, content
FROM updates
WHERE account_id != {$account_id}
and course_id in (select acr.course_id from accounts_courses_relationship acr where acr.account_id = updates.account_id)
ORDER BY datetime DESC
LIMIT 10

Categories