Join three Tables to create an Access Granted List - mysql

I have three tables I would like to join to pull off a MySQL Query that will check to see if a user has access to download photo. Each photo has its own access rights which can be one of the following.
Level one - any user can download the photo.
Level two - only users who have access can download the photo.
Table 1: FILES
FILE_ID is AUTO_INCREMENT
USER_ID is the identifier of the user that owns the file.
FILE_NAME is just the name of the photo.
FILE_ACCESS is the access rights to the 2 levels noted above.
FILE_ID | USER_ID | FILE_NAME | FILE_ACCESS
1 | 3 | 1279141923.jpg | 1
2 | 3 | 1279141925.jpg | 1
3 | 3 | 1279141927.jpg | 2
4 | 4 | 1279141929.jpg | 1
5 | 4 | 1279141931.jpg | 2
6 | 3 | 1279141933.jpg | 2
Table 2: USERS
USER_ID is AUTO_INCREMENT
USER_NAME is just the name of the file owner.
USER_ID | USER_NAME
1 | jack
2 | jill
3 | john
4 | mike
Table 3: ACCESS
ACCESS_ID is AUTO_INCREMENT
USER_ID is the identifier of the user that owns the file.
ALLOW_ID is the identifier of the user that has access all the file uploaded by user.
ACCESS_ID | USER_ID | ALLOW_ID
1 | 3 | 1
2 | 3 | 4
User Jack has access to download photos 1279141923.jpg, 1279141925.jpg and 1279141927.jpg that John has uploaded while Jill only has access to 1279141923.jpg and 1279141925.jpg
Jack gets access to all three as files 1 and 2 have access level one while he also gets access to download file 3 seeing that John has given him full access to all files uploaded by John.
SELECT a.file_name, b.user_name FROM files AS a
JOIN users AS b ON a.user_id = b.user_id
WHERE a.file_access = '1'
This MySQL Query gives me the following when Jill is signed in.
1279141923.jpg, john (owner name)
1279141925.jpg, john
1279141929.jpg, mike
I am now looking to introduce the ACCESS table into this Query, so that if Jill is signed in she is displayed with the same results as above will if Jack was signed he would get the following results.
1279141923.jpg, john (owner name)
1279141925.jpg, john
1279141927.jpg, john
1279141929.jpg, mike
1279141933.jpg, john
I hope I explain this right, as I sure need help with the last portion. I am not quite sure how to add this to the current Query. I can do it using multiple queries - but I prefer if possible would like to leave it as one.

SELECT DISTINCT(F.id), F.filename
FROM files F, ACCESS A
WHERE F.flie_access=1
OR (F.user_id=2)
OR (F.user_id = A.user_id AND A.allow_id=2);
So, you can access the file if it is:
level 1 (ie public)
or you are the owner
or you have a matching access entry in the 'ACCESS' table.
You seem to have granted access to ALL a users files to another user, you might want to consider a "USER_ACCESS" and a "FILE_ACCESS" table to grant other people access to either all your files or just a specfific file.

Tim, here is the modified query...
SELECT F.file_name, U.user_name
FROM files AS F
INNER JOIN users AS U USING (user_id)
LEFT OUTER JOIN access AS A USING (user_id)
WHERE (A.allow_id = 1 AND F.file_access IN (1, 2))
OR (A.allow_id IS NULL AND F.file_access = 1)
ORDER BY F.file_id
With multi-table JOINs, you have to be careful because the memory footprint of such join operations are significantly large, particularly if the tables are large -- which I am assuming they are (or will be as soon as your app picks up lot of user traffic). Sometimes, separating the query is not such a bad thing.
Just my 2 cents... HTH
Ashwin

Related

what is the best mysql database model for users with permissions [duplicate]

This question already has answers here:
How important are lookup tables?
(7 answers)
Closed 2 years ago.
i have a table of users where each user should have a list of permissions.
i have red in another answer that i should use a second table called permissions with a user id to assign the permission to a specific user.
userID | permission
--------|----------
1 | read
1 | write
1 | delete
with this solution i can just select all permissions with the id of the user and get all the permissions of a specific user.
what i think to be inefficient about this is the fact that all permissions will repeat for different users in this table.
userID | permission
--------|----------
1 | read
1 | write
1 | delete
2 | write <- repeating with different userid
in my case i will have at least 150 users and probably more than 100 permissions
which will make the table kind of big ?
i would guess this is a very common case to have. but i could not find a better way.
You can just create a new table with the list of all possible permissions and link those ID's with the User ID's.
- Permissions ( ID | Description )
- User Permissions ( UserId | PermissionID )
To select all the permissions for a single User
SELECT Permissions.*
FROM UserPermissions
LEFT JOIN Permissions ON Permissions.id = UserPermissions.PermissionId
WHERE UserPermissions.UserID = {userId}
Why do not make third table like this?
userID | other columns for users
--------|-------------------------
1 |
2 |
3 |
userID | permissionID
--------|----------
1 | 1
1 | 2
1 | 3
2 | 2
permissionID | permissionName
--------------|----------
1 | read
2 | write
3 | delete

Remove duplicate form select result

#edited
Ok. I will delete all role permissions and add them again.
Is it possible to combine all 4 queries into one?
I am creating a permission system.
Assumptions:
Each user can have more than one role
Each role can have more than one permission
Permissions can also be assigned directly to the user (they have a higher priority than permissions for roles)
The priority of permissions is:
role permission
denial of role
user permission
denied to user
Denying the user has the highest priority
The matter in PHP is quite simple:
I create an array with all permissions
I am getting permissions for a role (order by access)
I assign access, if it's denied, I overwrite access with denied
I do the same for user permissions
I assign access, if it's denied, I overwrite access with denied
This way I have the whole array with permissions for a specific user, e.g. $ user['permission']['delete_post'] // output: false || true
I need to do permission inspection now. This means which user has access to e.g. 'delete_post'
I have this database scructure:
Here fiddle with database: DB fiddle
I have problem with first query:
**Query #1**
=============================================
List of all roles related to permission with id 3 */
SELECT DISTINCT role_id, access, permission_id FROM role_permissions WHERE permission_id=3 ORDER BY role_id, access DESC;
| role_id | access | permission_id |
| ------- | ------ | ------------- |
| 5 | 0 | 3 |
| 8 | 1 | 3 |
| 10 | 1 | 3 |
| 10 | 0 | 3 |
As expected I should get
| role_id | access | permission_id |
| ------- | ------ | ------------- |
| 8 | 1 | 3 |
I cant add WHERE permission_id=3 AND access=1, because i getting result: role_id=8 and role_id=10, but role_id=10 doesn't really have access.
One way is to do all appropriate joins between various tables based on their relationships, and then do aggregation based filtering using GROUP BY with HAVING clause.
Following query will give you all the users who has access allowed for a given permission id (more explanation in comments inside the query below - there may be requirement of more fiddling with the logic; check and comment if needed):
Query - View on DB Fiddle
SELECT
u.user_id,
u.name
FROM users AS u
-- Left Join to get access (if defined for input permission id)
LEFT JOIN user_permissions AS up
ON up.user_id = u.user_id
AND up.permission_id = 3
-- Join to Roles; assuming every user has atleast one role
-- Change this to LEFT JOIN if it is possible that user can have NO role
JOIN user_roles AS ur
ON ur.user_id = u.user_id
-- Left Join to get access defined for input permission id for roles
LEFT JOIN role_permissions AS rp
ON rp.role_id = ur.role_id
AND rp.permission_id = 3
GROUP BY u.user_id, u.name
HAVING
-- first priority if user specific access allowed
-- if data is sane then up.access will have same value
-- across all rows in a group for user
MAX(up.access) = 1
OR
-- second priority is not have a single role with
-- denied permission AND
-- atleast one role exists with allowed permission
( NOT SUM(rp.access = 0)
AND
SUM(rp.access = 1)
);
Result
| user_id | name |
| ------- | ---------------- |
| 4 | Cyrus Gomez |
| 7 | MacKensie Morton |
| 13 | Nadine Taylor |
| 15 | Ezekiel Bonner |
| 17 | Ciaran Turner |
| 35 | Olga Dominguez |
| 38 | Lucas Pierce |
You can add a WHERE condition as below-
SELECT DISTINCT role_id, access, permission_id
FROM role_permissions
WHERE permission_id=3
AND role_id NOT IN (
SELECT DISTINCT role_id
FROM role_permissions
WHERE access = 0
AND permission_id=3 -- New condition added
)
ORDER BY role_id, access DESC;

MySQL Select using one table's data as column for query

I have a database table which each row represents a column of data I'd like pulled for a query. My table is:
tracked_pages
page_id | page_url | page_name
-------------------------------------
1 | users.php | Users
2 | auctions.php | Auctions
3 | receipts.php | Receipts
So essentially, I have another table that tracks each time a page is accessed by a logged-in user. But these 3 pages are the only ones I'm really interested in right now.
This is not my actual query, but just to show how I'd write it if I just hand-wrote it, I'd do something like:
Select user.id, count(clicks1.id) As user_clicks, count(clicks2.id) As auction_clicks, count(clicks3.id) As receipt_clicks
From users
Left Join clicks As clicks1 On (users.id = clicks1.id And clicks1.page = 'users.php')
Left Join clicks As clicks2 On (users.id = clicks2.id And clicks2.page = 'auctions.php')
Left Join clicks As clicks3 On (users.id = clicks3.id And clicks.page = 'receipts.php')
Group By users.id
So I'd expect output similar to:
Output
user_id | user_clicks | auction_clicks | receipt_clicks
-------------------------------------------------------
1 | 54 | 16 | 16
2 | 27 | 22 | 12
3 | 32 | 24 | 38
However, I'd like the query to pull each column from the database instead of having to join the same table manually. The reason is because the tracked_pages table can be updated through a management area and I don't want to have to go update the code each time they want to track a different page.
I know I could just perform a query to get the users, then as I loop through that I could perform another query to get the page counts, but I'm really wanting to make it just one query.
Any help would be appreciated.
Thanks,
James

data model for user register system with blacklist and favoritelist?

can someone show me data model for user register system which user can favorite or blacklist(ban) each other?i am looking for best way but no one answered my questions well until now.by best way i mean we have less join for programing.
my user table until now
uid(pk) | name | family | blah blah blah ..
i just dont know how to desgin this favorite and blacklist which we just have one join or less?
i think it must be a one to many relation but how should i design this?
you could have a second table listing the blacklists and/or favourites
table
id(pk) | userid_src(fk) | userid_dst(fk) | type
If user A blacklists user B and then user A favourites user C and user B and C favourites each other you will have this where A is id 1, B id 2 and C id 3
id(pk) | userid_src(fk) | userid_dst(fk) | type
1 | 1 | 2 | blacklist
2 | 1 | 3 | favourite
3 | 2 | 3 | favourite
4 | 3 | 2 | favourite
EDIT: to retrieve the info of each user :
SELECT * from users
LEFT JOIN users_connections as con ON users.uid = con.userid_src
LEFT JOIN users as userCon ON userCon.uid = con.userid_dst
WHERE users.id = XXX
XXX will be the id of your current user. You will then retrieve each connections between your current user and each user he blacklisted or added as favourite.
One place to look for ready made data models is databaseanswers.com. Here is the page that catalogs their data models.
http://www.databaseanswers.org/data_models/index.htm
There's a section on Social Networking Sites, but it's pretty sparse.

mysql optimize tables

I want to create a friends system (something like in facebook).
I want to save relationship data in MySql, but I do not know which way is better:
To save everysingle relationship as a single entry, such as:
id | people1 | people2
1 | john | maria
2 | john | fred
3 | maria | fred
(there i declare relationships between all of these 3 peoples)
To save everyone name and list his friends:
id | people | friends
1 | fred | mary, john
2 | mary | john, fred
3 | john | fred, mary
Or maybe there is better way?
No Dear,
you just need one single table for make friend relationship. structure is following i have used
id (primary key) | my_id( integer logged user id ) | friend_id ( integer user id of another user he will receive friend request from logged user)
like we have two users in our users table then we have two entries for both user to make relation with each other
id | name | age
1 | vipan | 12
2 | karan | 12
then entry should be
id | my_id | friend_id
1 1 2
2 2 1
Please don't vote down in any case but i have use this table structure in my site and this is same structure used in joomsocial this is best table structure i think so i use it and please don't use comma separated values in table they will make problem in joins and relationship in some cases
Please see 4 number comment in this following link of post
Separate comma separated values from mysql table
The first one is the best no doubt cause the second one would not respect the first normal form.
You have to avoid multiple values in the same column cause it will get really painful to edit
Here's the link about database normalization. Most of the time, we respect the third normal form cause it's a good compromise between normalization and performance.
Also, like Randy said, you have to use the IDs so then you can link them with a foreign key.