SQL Server database design: Advice on many to many relationship - sql-server-2008

I am setting up a database and am at a place where I am confusing myself on some many-to-many tables. I am looking for some advice on the best way to design this for performance and scalability.
I will lay out an example of my setup and what I am trying to do below.
I have the main object table...
Account
AccountID| AccountName
-----------------------
1 | First Account
...and then the child objects to be given permissions to.
Page
PageID | PageName
------------------
1 | First Page
Control
ControlID | ControlName
-----------------------
1 | First Control
MenuItem
MenuItemID | MenuItemName
-------------------------
1 | Menu Item 1
I have a permissions table for read/write etc...
Permission
PermissionID | PermissionName
------------------------------
1 | CanRead
2 | CanWrite
3 | CanDelete
So I'm trying to tie the permission table in a many-to-many between the main objects and the child objects. I will list below what I've come up with and why I don't think it's right.
One table to rule them all
PermissionAccount
AccountID | PermissionID | ControlID | MenuItemID | PageID
----------------------------------------------------------
1 | 2 | NULL | NULL | NULL
2 | NULL | 2 | NULL | NULL
*this solution is just ugly. There can be many MenuItemID's assigned to a single account
One table for every object
PermissionAccountControl
AccountID | ControlID | PermissionID
------------------------------------
1 | 1 | 1
1 | 2 | 1
PermissionAccountMenuItem
AccountID | MenuItemID | PermissionID
-------------------------------------
1 | 1 | 2
1 | 2 | 1
PermissionPage
AccountID | PageID | PermissionID
---------------------------------
1 | 1 | 3
1 | 2 | 1
I am leaning more toward option two. Any thoughts or suggestions are appreciated.

The latter is correct
This is 4th/5th normal form when designing a database

You could also use polymorphic association
+------------+------------+----------+--------------+
| AccountID | ObjectType | ObjectID | PermissionID |
+------------+------------+----------+--------------+
| 1 | Page | 1 | 1 |
| 1 | MenuItem | 1 | 2 |
+------------+------------+----------+--------------+
This method might be useful if you need to handle permissions for additional objects in your software at a later point of time. Then you won't have to create new tables or add new columns in existing tables if you use this table structure.

Related

MySql add relationships without creating dupes

I created a table (t_subject) like this
| id | description | enabled |
|----|-------------|---------|
| 1 | a | 1 |
| 2 | b | 1 |
| 3 | c | 1 |
And another table (t_place) like this
| id | description | enabled |
|----|-------------|---------|
| 1 | d | 1 |
| 2 | e | 1 |
| 3 | f | 1 |
Right now data from t_subject is used for each of t_place records, to show HTML dropdowns, with all the results from t_subject.
So I simply do
SELECT * FROM t_subject WHERE enabled = 1
Now just for one of t_place records, one record from t_subject should be hidden.
I don't want to simply delete it with javascript, since I want to be able to customize all of the dropdowns if anything changes.
So the first thing I though was to add a place_id column to t_subject.
But this means I have to duplicate all of t_subject records, I would have 3 of each, except one that would have 2.
Is there any way to avoid this??
I thought adding an id_exclusion column to t_subject so I could duplicate records only whenever a record is excluded from another id from t_place.
How bad would that be?? This way I would have no duplicates, so far.
Hope all of this makes sense.
While you only need to exclude one course, I would still recommend setting up a full 'place-course' association. You essentially have a many-to-many relationship, despite not explicitly linking your tables.
I would recommend an additional 'bridging' or 'associative entity' table to represent which courses are offered at which places. This new table would have two columns - one foreign key for the ID of t_subject, and one for the ID of t_place.
For example (t_place_course):
| place_id | course_id |
|----------|-----------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 3 |
As you can see in my example above, place 3 doesn't offer course 2.
From here, you can simply query all of the courses available for a place by querying the place_id:
SELECT * from t_place_course WHERE place_id = 3
The above will return both courses 1 and 3.
You can optionally use a JOIN to get the other information about the course or place, such as the description:
SELECT `t_course`.`description`
FROM `t_course`
INNER JOIN `t_place_course`
ON `t_course`.`id` = `t_place_course`.`course_id`
INNER JOIN `t_place`
ON `t_place`.`id` = `place_id`

Mysql find root parent of shared post

I have to manage sharing-post like on Facebook here is how I intend to do it.
I have a table which contains articles with a parent_id field and I would like to change state of all shared articles to 0 when parent is deleted(when parent state = 0)
articles
+----+-----------+--------------+-------+--------+
| id | content | user_id | parent_id | state |
+----+-----------+--------------+-------+--------+
| 1 | content | 2 | null | 1 |
| 2 | content | 5 | null | 1 |
| 3 | content | 4 | 2 | 1 |
| 4 | content | 6 | null | 1 |
| 5 | content | 7 | 1 | 1 |
| 6 | content | 1 | 3 | 1 |
| ...| ... | ... | ... | ... |
+----------------+---------+------------+--------+
Example according to the articles table above:
User 4 shares article 2 and user 1 shares article 3 which is a child of the article 2. So when article 2(root article) is deleted(his state change to 0) all child articles(3 & 6) state have to also change to 0.
How to accomplish this ?
Any other way to manage sharing post will be great appreciated
I think you can program a trigger on delete:
Documentation: https://dev.mysql.com/doc/refman/5.7/en/create-trigger.html
There's an example:
BEFORE INSERT ON articles
FOR EACH ROW
BEGIN
UPDATE articles SET parent_id = 0 WHERE parent.id = OLD.parent_id;
END
If you need the trigger on multiple tables, you can program a stored procedure and call it on the trigger instead of programming the query on every table.
Also, if you want to avoid having lost / unlinked records, you can have a foreign key if you use INNODB tables. With a foreign key, you cannot insert a parent_id who not exists, or you cannot delete a register if they have any childs.
You can check documentation about foreign keys here: https://dev.mysql.com/doc/refman/5.7/en/create-table-foreign-keys.html

Mysql: Change set column on update condition?

I'm trying to figure out the best way to update one of two fields in a table. I have a personrelationship table and it links two people and I would like to have each person be able to set the relationship type on their end.
PersonRelationship Table
id int
user1_id int
user2_id int
user1_reltype /* boss, manager, etc */
user2_reltype
Depending on whether the current user is either user1_id or user2_id in the table, I need to update the user_reltype accordingly. So basically if current userid is in the user1_id field then update user1_reltype otherwise update the user2_reltype.
Since you want each user to be able to independently manage their half of the relationship, you can simplify your table structure
--------------------------------------
| initiator_id | reltype | target_id |
When a person with ID 5 (the 'initiator') marks person with ID 9 (the 'target') as a friend, the table will contain:
---------------------------------------
| initiator_id | reltype | target_id |
+--------------+----------+-----------+
| 5 | 'friend' | 9 |
If person 9 later initiates a 'boss' connection with person 5, the entry can be created without interfering with the row previously created by person 5:
--------------------------------------
| initiator_id | reltype | target_id |
+--------------+---------+_----------+
| 9 | 'boss' | 5 |
This approach will make your table easy to read and your queries easy to write.
Extra:
If you do not already have it, consider creating another table to track relationship types ('reltype'):
-----------------
| id | type |
+----+----------+
| 1 | 'friend' |
| 2 | 'boss' |
and replace the string reltype's in the relationship table with foreign keys.
---------------------------------------
| initiator_id | reltype | target_id |
+--------------+----------+-----------+
| 5 | 1 | 9 |
| 9 | 2 | 5 |

Application specific MySQL table Structure

I have a question about my DB table structure. I want to know if i'm on the right track or if I have missed a good alternative. Here is the case:
To make it easy to read, I haven't pasted the full contents as my question is only about the structure.
2 tables:
1: id (AI), task
2: id, name, task
Table 1 presents dynamic check-boxes which can be altered by an admin panel so the contents would be like this
1 task1
2 task2
5 task5
(3 & 4 are missing cause the administrator deleted those records).
In table number two are the people who should do the tasks from table 1. And the goal is that the tasks wich are not checked will be displayed.
So the contents of table 2 would be:
1 Name1 1,5
2 Name2 1,2
3 Name3 1,2,5
The numbers in table 2 represent the checked boxes from table 1. So with a query i can compare the numbers from table 2 with the id's from table 1 and display the missing ids as "todo".
In my opinion this looks very overdone, and there must be an easier way to create dynamic options which can be compared and stored as a todo.
Suggestions are welcome!
I suggest you to use basic structure for many-to-many relationship:
tasks users user_tasks
+----+-----------+ +----+-------+ +---------+---------+
| id | name | | id | name | | user_id | task_id |
+----+-----------+ +----+-------+ +---------+---------+
| 1 | Buy milk | | 1 | John | | 1 | 2 |
| 2 | Get drunk | | 2 | Tim | | 3 | 2 |
| 3 | Have fun | | 3 | Steve | | 2 | 4 |
| 4 | Go home | +----+-------+ | 3 | 4 |
+----+-----------+ +---------+---------+
And you can fetch unassigned tasks using following query:
SELECT
tasks.*
FROM
tasks
LEFT JOIN
user_tasks
ON (tasks.id = user_tasks.task_id)
WHERE
user_tasks.user_id IS NULL
You also can fetch users who have no assigned tasks:
SELECT
users.*
FROM
users
LEFT JOIN
user_tasks
ON (users.id = user_tasks.user_id)
WHERE
user_tasks.user_id IS NULL
Hope this will help you.

Data Entry Tracking (Database Design)

I have developed a website (PHP) that allow staffs to add records on to our system.
Staffs will be adding thousands of records into our database.
I need a way to keep track of what record have been done and the process/status of record.
Here a number of Teams I could think of:
Data Entry Team
Proof Reading Team
Admin Team
When staff (Data Entry Team) completed a record - he/she will then click on the Complete button. Then somehow it should asssign to 'Proof Reading Team' automatically.
A record need to be checked twice from a Proof Reading Team. If StaffB finish proof reading then another member from Proof Reading Team need to check it again.
When Proof reading is done, Admin Team will then assign "Record Completed"
In a few months later record might need to be updated (spelling mistake, price change, etc) - Admin might to assign record to Data entry team.
Is this good data entry management solution? How do I put this into Database Design perspective?
Here what I tried:
mysql> select * from records;
+----+------------+----------------------+
| id | name | address |
+----+------------+----------------------+
| 1 | Bill Gates | Text 1 Text Text 1 |
| 2 | Jobs Steve | Text 2 Text 2 Text 2 |
+----+------------+----------------------+
mysql> select * from staffs;
+----+-----------+-----------+---------------+
| id | username | password | group |
+----+-----------+-----------+---------------+
| 1 | admin1 | admin1 | admin |
| 2 | DEntryA | DEntryA | data_entry |
| 3 | DEntryB | DEntryB | data_entry |
| 4 | PReadingA | PReadingA | proof_reading |
| 5 | PReadingB | PReadingB | proof_reading |
+----+-----------+-----------+---------------+
mysql> select * from data_entry;
+----+------------+-----------+------------------------+
| id | records_id | staffs_id | record_status |
+----+------------+-----------+------------------------+
| 1 | 2 | 3 | data_entry_processiing |
| 2 | 2 | 3 | data_entry_completed |
| 3 | 2 | 4 | proof_read_processing |
| 4 | 2 | 4 | proof_read_completed |
| 5 | 2 | 5 | proof_read_processing |
| 6 | 2 | 5 | proof_read_completed |
+----+------------+-----------+------------------------+
Is there alternative better solution of database design?
i think design it's well done. but may be you want to separate group into groups table, and record_status into status table. If you're storing a lot of records you would store a lot of useless information, at least create an enum type for record_status field and group field
table: groups
id - name 1 - admin 2 - data_entry 3 - proof_reading
...
table: status
id - name 1 - data_entry_processing ...
and if you want the users to be in different groups at a time, you could create users_group table
table: user_groups
group_id - user_id 1 - 1 2 - 1 1 - 4 3 -
4 4 - 4 ....
Hope this helps