Simple database design - database

I am so poor on database design. I want to ask you to make sure !
Example I have two table look like this:
1) 2)
tb_users: | tb_users:
---------- | -------------
- user_id | - user_id
- user_name | - user_name
- role_id | ===============
============ | tb_role:
tb_roles: | --------------
----------- | - role_id
- role_id | - role_name
- role_name | - user_id
============ | ================
Which one is right, 1) or 2)?
Thank for any answer. I really don't understand. If you have any key to remember, please tell me also.

The second one, but read this whole answer please for a comment at the end.
Look at it this way: "Users" can stand alone. They do not need roles. However, "Roles" require users or they have no meaning.
Having said that, I think both are wrong. The third option is to have 2 main tables, and a 3rd table that allows you to join the others.
The User table uniquely identifies users. The Role table identifies roles. The User_Role table says which user has which role.
So something like this:
USER
----
User_Id
User_Name
ROLE
----
Role_Id
Role_Name
USER_ROLE
---------
User_Id
Role_Id
That allows for each user to have as many roles as required, and it allows for roles to be assigned to as many users as required.

Related

The conventional way of exporting many to many relation in a single csv file

I have a database which has the following many to many relation:
Group table:
group_id | group_name
User table:
user_id | user_name
User_group table:
user_id | group_id
I want to create a single csv file that will incorporate those 3 tables. I can ignore the ids from database. The csv should be easily parseable in order to recreate the database with the same data. I am thinking about the format that csv should have. Right now my idea is to do the following
user_name | group
-------------------
bob | group_a
| group_b
sam | group_a
However, I am not sure what is more conventional to do. Maybe list all the groups separated by comma/space?

How to properly store login info in database

I wanna to design an authentication database which has the following functions:
I used Bcrypt so I need to store the salted password in the dB
I only allow user login to one device at a time, so I need to check if the user is currently logged in. (is_logged_in)
I need users to verify their email/phone before adding them into the account (email_verified, phone_verified)
I want to store all the user's info (firstName, lastName, dateOfBirth, etc.)
I want users to create at most three security questions/answers before using
I want to store users' passed logged in info (Time, location, etc.)
I want to share this database between my multiple apps, i.e. use one account to log in my multiple apps
So I Designed the following tables (Primary Key is in bold):
Authentication_Info
UID | Password_salt | Is_logged_in | Email_Verified | Phone_Verified
User_Info
UID | FirstName | LastName | Email | Location | RegisterDate | PhoneNumber | DateOfBirth | isEnabled
SecurityQ/A
UID | SID | SecurityQuestion | SecurityAnswer
Passed_login_info
UID | Time | Location | IP | Device
After I changed them to 3NF, I got the following tables (Again, primary Key is in bold):
Authentication_Info
UID | Password_salt | Is_logged_in
User_Info
UID | FirstName | LastName | Email_id | Location | RegisterDate | Phone_id | DateOfBirth
SecurityQ/A
UID | SID | SecurityQuestion | SecurityAnswer
Passed_login_info
UID | PID | Time | Location | IP | Device
Token_Verification
Verification_id | UID | TempToken | Expire_Time
Is this a good design? Any mistakes I made?
Since I'm not familiar with the intended use of the application, I can't say for sure if the advice below is applicable in this situation, but regardless, here are some suggestions:
Regarding the Token_Verification table, I believe only three fields are required: UID, TempToken, Expire_Time. I don't see the need for Verification_id.
The Authentication_Info table does not need a field for Is_logged_in because we can check if a UID is logged-in by searching the Token_Verification table for a valid TempToken. Thus, this field can be removed.
The Password_salt field in the Authentication_Info table should be separated into Salt and SaltedHash_Password. When a password arrives for validation, you will have to prepend the salt, hash the (salt + password) and compare against the stored value.
In the SecurityQ/A table, you should treat SecurityAnswer as if it were a password. Each security answer should be stored with a unique random salt. Thus, the fields of the table should be UID, SID, SecurityQuestion, Salt, SaltedHash_SecurityAnswer.
The tables I haven't mentioned above look fine to me :)
(\(\
).. \
\Y_, '-.
) '.
| \/ \
\\ |\_ |_
((_/(__/_,'.
(,----'

What is a sensible approach for enabling/disabling features on a database object?

Say I have a User database table with the regular username, password, email fields. What is a sensible way to add additional boolean fields that enable/disable features for any given user.
e.g.,
user_can_view_page_x
user_can_send_emails
user_can_send_pms
etc
Adding a bunch of boolean columns to the existing user table seems like the wrong way to go.
Yes, I would think that this is the wrong approach.
I would rather create a
User_Features Table
with columns something like
UserID
FeatureName
And check if a given user has the feature in question enabled/entered in the table.
You could even go as far as creating a Users_Groups table, where users are also assosiated with groups and features can be inherited/disallowed from group settings.
I would use three tables.
One is your existing user table:
USER table
----
user_id (pk)
name
email
...
Another is a table containing possible user privileges:
PRIVILEGE table
----
privilege_id (pk)
name
Lastly is a join table containing an entry for each privilege setting for each user:
USER_PRIVILEGE table
----
user_id (pk) (fk)
privilege_id (pk) (fk)
allowed
Here is some sample data for two users, one with the send email privilege and the send pms privilege and another with a view page privilege:
USER data
USER_ID NAME EMAIL
------- ----- -------------------
1 USER1 user1#somewhere.com
2 USER2 user2#somewhere.com
PRIVILEGE data
PRIVILEGE_ID NAME
------------ -----------
1 view_page_x
2 send_email
3 send_pms
USER_PRIVILEGE data
USER_ID PRIVILEGE_ID ALLOWED
------- ------------ -------
1 1 'N'
1 2 'Y'
1 3 'Y'
2 1 'Y'
2 2 'N'
2 3 'N'

CakePHP find with 2 HABTM relationships

I'm working on project that run on CakePHP 2 framework. In this application I have:
models:
Wallnote, User, Group
relationships:
Group HABTM User
Wallnote HABTM User
Wallnote HABTM Group
tables:
wallnotes
- id
- user_id (owner id)
- ...
users_wallnotes
- user_id
- wallnote_id
groups_wallnotes
- group_id
- wallnote_id
groups_users
- group_id
- user_id
I'm using the relationships "Wallnote HABTM User" and "Wallnote HABTM Group" as a filter i.e. user_id/group_id(6) wallnote_id(10) mean, that wallnote with id 10 will be visible for user with id 6, respectively for all users in group with id 6.
I would like to find all wallnotes matching these conditions:
logged user is an owner of this wallnote OR
wallnote was shared with logged user -> record in table users_wallnotes OR
wallnote was shared with some group and logged user is member of this group
It is possible to do this using find() function?
Thanks for answer.
You have two roles: group and user.
it's very difficult to handle it with User and Group separately...
I think the standard way is you have unique role for each user and group, for handling this you must have this tables:
wallnotes
- id
- user_id (owner id)
- ...
roles
- id
- user_id
- group_id
(each rows in this table have group_id or user_id , no both of them)
roles_wallnotes
- id
- role_id
- wallnote_id
when you create a user(or group) you must create a role for it. So all groups and users, now have unique id.
for example you have 2 groups(with 1,2 ids) and 4 users(with 1,2,3,4 ids), then:
users :
|id|name |
---|------
1 | user1
2 | user2
3 | user3
4 | user4
groups :
|id|name |
---|------
1 | group1
2 | group2
roles :
|id| user_id | group_id |
---|---------|----------|
1 | 1 | null |
2 | 2 | null |
3 | 3 | null |
4 | 4 | null |
5 | null | 1 |
6 | null | 2 |
now you must have this relations:
Role hasOne Group
Role hasOne User
Wallnote HABTM Role
with this solution you can easily use find function for retrieve your data you need...

DB Data migration

I have a database table called A and now i have create a new table called B and create some columns of A in table B.
Eg: Suppose following columns in tables
Table A // The one already exists
Id, Country Age Firstname, Middlename, Lastname
Table B // The new table I create
Id Firstname Middlename Lastname
Now the table A will be look like,
Table A // new table A after the modification
Id, Country, Age, Name
In this case it will map with table B..
So my problem is now i need to kind of maintain the reports which were generated before the table modifications and my friend told me you need to have a data migration..so may i know what is data migration and how its work please.
Thank you.
Update
I forgot to address the reporting issue raised by the OP (Thanks Mark Bannister). Here is a stab at how to deal with reporting.
In the beginning (before data migration) a report to generate the name, country and age of users would use the following SQL (more or less):
-- This query orders users by their Lastname
SELECT Lastname, Firstname, Age, Country FROM tableA order by Lastname;
The name related fields are no longer present in tableA post data migration. We will have to perform a join with tableB to get the information. The query now changes to:
SELECT b.Lastname, b.Firstname, a.Country, a.Age FROM tableA a, tableB b
WHERE a.name = b.id ORDER BY b.Lastname;
I don't know how exactly you generate your report but this is the essence of the changes you will have to make to get your reports working again.
Original Answer
Consider the situation when you had only one table (table A). A couple of rows in the table would look like this:
# Picture 1
# Table A
------------------------------------------------------
Id | Country | Age | Firstname | Middlename | Lastname
1 | US | 45 | John | Fuller | Doe
2 | UK | 32 | Jane | Margaret | Smith
After you add the second table (table B) the name related fields are moved from table A to table B. Table A will have a foreign key pointing to the table B corresponding to each row.
# Picture 2
# Table A
------------------------------------------------------
Id | Country | Age | Name
1 | US | 45 | 10
2 | UK | 32 | 11
# Table B
------------------------------------------------------
Id | Firstname | Middlename | Lastname
10 | John | Fuller | Doe
11 | Jane | Margaret | Smit
This is the final picture. The catch is that the data will not move from table A to table B on its own. Alas human intervention is required to accomplish this. If I were the said human I would follow the steps given below:
Create table B with columns Id, Firstname, Middlename and Lastname. You now have two tables A and B. A has all the existing data, B is empty .
Add a foreign key to table A. This FK will be called name and will reference the id field of table B.
For each row in table A create a new row in table B using the Firstname, Middlename and Lastname fields taken from table A.
After copying each row, update the name field of table A with the id of the newly created row in table B.
The database now looks like this:
# Table A
-------------------------------------------------------------
Id | Country | Age | Firstname | Middlename | Lastname | Name
1 | US | 45 | John | Fuller | Doe | 10
2 | UK | 32 | Jane | Margaret | Smith | 11
# Table B
------------------------------------------------------
Id | Firstname | Middlename | Lastname
10 | John | Fuller | Doe
11 | Jane | Margaret | Smith
Now you no longer need the Firstname, Middlename and Lastname columns in table A so you can drop them.
voilĂ , you have performed a data migration!
The process I just described above is but a specific example of a data migration. You can accomplish it in a number of ways using a number of languages/tools. The choice of mechanism will vary from case to case.
Maintenance of the existing reports will depend on the tools used to write / generate those reports. In general:
Identify the existing reports that used table A. (Possibly by searching for files that have the name of table A inside them - however, if table A has a name [eg. Username] which is commonly used elsewhere in the system, this could return a lot of false positives.)
Identify which of those reports used the columns that have been removed from table A.
Amend the existing reports to return the moved columns from table B instead of table A.
A quick way to achieve this is to create a database view that mimics the old structure of table A, and amend the affected reports to use the database view instead of table A. However, this adds an extra layer of complexity into maintaining the reports (since developers may need to maintain the database view as well as the reports) and may be deprecated or even blocked by the DBAs - consequently, I would only recommend using this approach if a lot of existing reports are affected.

Resources