Table Rows or Columns - mysql

I have a project which requires me to setup custom privileges, divided in three categories "Admin, Manager, User"
My regular approach is to distribute Privileges in one table as headers, then add a raw for each category with 0 or 1 to activate or deactivate the privilege for a group like follows:
id|name|can_do_this|can_do_that
1|admin|1|1
2|manager|1|0
3|user|0|0
however my professor requested that each privilege to be added separately per user no per group like follows:
id|user_id|privilege|active
1,1,can_do_this,1
2,1,can_do_that,1
3,2,can_do_this,1
4,2,can_do_that,0
my question, for the sake of my sanity.. which is more efficient? his point is that IF we needed to add a new privilege we won't need to ALTER the table to add a new column.
hope this question makes sense.

To me, this is a very simple data modeling issue. You have two "entities" in your data model:
users
privileges
This suggests that each one should have its own table.
Because this is a many-to-many relationship (many users can have a given privilege, one user can have many privileges), a third table is normally used for expressing the relationship; this is often called a "junction table" or "association table".
Your professor gives one very good reason for expressing the values in rows rather than columns: The ability to add new privileges.
I can add a few more:
The userPrivileges table can have a createdOn column so you know when the privilege took effect.
The userPrivileges can have a createdBy column, so you know who granted the privilege.
The userPrivileges table can have a suspended column, so you can temporarily suspend privileges.

I would suggest you the second one, because like that as your teacher says you don't need to Alter the table. Altering the table would mean adding a new 1 or 0 for each member in your table (you could use a default value but you will still need to change the values for those users that need the privilege).
The way your teachers says you could have another table with all the privileges, and use a foreign key.
That way you could add a new privilege and asign it to the users they need it with a default value of "1", and if you need to revoke the privilege change it for a "0". No innecesari rows will be added for default, that in small tables is not a problem but for bigger ones it is.
id |user_id |privilege |active
1 1 can_do_this 1
2 1 can_do_that 1
3 2 can_do_this 1
4 2 can_do_that 0

As per my experience, If you don't want to add the extra column every time whenever a new privilege required to be updated in apps. Then go with second option.
Benefit:
Option-1: There will be no redundant data for group and that can be managed easily as you can apply the unique constraint on the group name and hence it will not requires the insert every time.
Option-2: You don't need to add the alter the table you can simply verify does new permission is already there or not, if not then simply add a new insert or update the existing permission.
Dis-advantage:
Option-1: You need to alter the table every time whenever new permission comes in.
Option-2: Whenever you want to add the new permission for a group first you have to identify that the records already exists or not, then you have to insert into table. And while validating the is also a bit of complex compare to the first option.
So, both has its advantage and disadvantages. If you think from data redundant perspective then go with option-1, else go with option-2.
I would prefer to go with option-1 as per my knowledge, and what I will do I just maintain a extra table which identify the permission and physical column between them, and I will make it generic.
ThatsAll!!!

Related

Mysql: possible to add constraint that prevents a one to many relation from having less than certain number of relations?

I have a user table that has many, say user_property table, where the foreign user_id is stored in the user_property table.
Now is it possible to add constraint so that a user should have at least one user property? So when a user have five properties, he can delete it one by one, but when there is only one property left, he can not delete it? I tried Googling but I am not even sure what is the search keyword for this.
The reason is, I want to avoid checking if a user have one property remaining only from the application layer, because it reads from replica, the read and write might not be synchronized, and on certain condition the user might accidentally delete all properties.
Any suggestion or different approaches is appreciated.
I don't think you can do this with a constraint. The problem is handling new users. You cannot insert a new user, because it has no properties. You cannot insert a new property, because the user reference is not valid. Ouch!
One solution involves triggers. The idea is the following:
Add to the the users table a column for the number of current properties.
Add to the users table a column for the maximum number of properties ever.
Default the two values to 0 for new users.
Add a check constraint (or trigger) that when the maximum is > 0 then the current number has to be > 0.
In any database, you need to implement the first two counts using triggers (on user_property). MySQL does not support check constraints, so the last condition also requires a trigger.
There is no constraint in SQL that does what you describe.
A foreign key constraint would ensure that every row in user_property must reference a row that exists in the user table.
But there is no constraint in SQL that does the reverse: ensure every user is referenced by at least one row in user_property.
A CHECK constraint has been mentioned by some other comments and answers. But a CHECK constraint can reference only columns of the same row. It can't reference other rows of the same table or different tables.
The most straightforward solution is to handle this in application code. That is:
Implement a function that INSERTs to user, while making sure there's also an INSERT of the first row to user_property.
Implement a function that DELETEs from user_property, but first check if it's would leave zero properties for the given user_id. If so, return an error instead of deleting the user property.
Implementing such data integrity rules in application code comes with a risk, of course. What if you have multiple apps that access the same database? You need to implement the same rules in different apps. Perhaps even in different programming languages. Sounds like a PITA.
Nevertheless, not all business rules can be implemented with simple SQL declarative constraints.

MySQL - Storing Default Values for System

I have a few tables storing their corresponding records for my system. For example, there could be a table called templates and logos. But for each table, one of the rows will be a default in the system. I would have normally added a is_default column for each table, but all of the rows except for 1 would have been 0.
Another colleague of mine sees another route, in which there is a system_defaults table. And that table has a column for each table. For example, this table would have a template_id column and a logo_id column. Then that column stores the corresponding default.
Is one way more correct than the other generally? The first way, there are many columns with the same value, except for 1. And the second, I suppose I just have to do a join to get the details, and the table grows sideways whenever I add a new table that has a default.
The solutions mainly differ in the ways to make sure that no more than one default value is assigned for each table.
is_default solution: Here it may happen that more than one record of a table has the value 1. It depends on the SQL dialect of your database whether this can be excluded by a constraint. As far as I understand MySQL, this kind of constraint can't be expressed there.
Separate table solution: Here you can easily make sure by your table design that at most one default is present per table. By assigning not null constraints, you can also force defaults for specific tables, or not. When you introduce a new table, you are extending your database (and the software working on it) anyway, so the additional attribute on the default table won't hurt.
A middle course might be the following: Have a table
Defaults
id
table_name
row_id
with one record per table, identified by the table name. Technically, the problem of more than one default per table may also occur here. But if you only insert records into this table when a new table gets introduced, then your operative software will only need to perform updates on this table, never inserts. You can easily check this via code inspection.

MySql Soft delete

I have an existing application (with MySQL DB).
I just got a new requirement where I need to delete some records from one of main entity. I dont want to apply hard delete here as its risky for whole application. If I use soft delete I have to add another field is_deleted and because of that i have to update all my queries (like where is_deleted = '0').
Please let me know if there is any smarter way to handle this situation. I have to make changes in half of the queries if I introduce a new flag to handle deletes.
Your application can run without any changes. MySQL is ANSI-SPARC Architecture compliant . With external schema you achieve codd's rule 9 "Logical data independence":
Changes to the logical level (tables, columns, rows, and so on) must
not require a change to an application based on the structure. Logical
data independence is more difficult to achieve than physical data
independence.
You can rename your tables and create views with original table names. A sample:
Let's supose a table named my_data:
REMAME TABLE my_data TO my_data_flagged
ALTER TABLE my_data_flagged
ADD COLUMN is_deleted boolean NOT NULL default 0;
CREATE VIEW my_data AS
SELECT *
FROM my_data_flagged
WHERE is_deleted = '0'
Another way is create a trigger and make a copy of erased rows in independent table.
Four suggestions:
Instead of using a bit called is_deleted, use a dateTime called something like deleted_Date... have this value be NULL if it is still active, and be a timestamp for the deletion date otherwise. This way you also know when a particular record was deleted.
Instead of updating half of your queries to exclude deleted records, create a view that does this filtering, and then update your queries to use this view instead of applying the filtering everywhere.
If the soft deleted records are involved in any type of relationships, you may have to create triggers to ensure that active records can't have a parent that is flagged as deleted.
Think ahead to how you want to eventually hard-delete these soft-deleted records, and make sure that you have the appropriate integrity checks in place before performing the hard-delete.

Database implementation in order to save user activity information

I am using Ruby on Rails 3 and MySQL.
In my project I would like to create an activity-stream "module" in order to save each user action information in a dedicated user table. That is, to create a database table for each user.
Is it a good approach to create a database table for each (new registered) user in my application?
No, it is not a good approach. Why would you create a separate tables with all the same fields? Just add user_id to your table and store all info for every user in there.
I do something similar, and it's not necessary to create a whole table for each user. For example, I have a table called "user_actions", and in it there is a column, "user_id".
The relationships are:
User has_many :user_actions
UserAction belongs_to :user
And you're done. Let the foreign-key relationship that comes naturally take care tying the specific action to a specific user.
Once you do that, you only need to decide:
Which actions cause an entry to be added?
How long should you retain the data (1 week, 6 months)?
For example, on my site, I keep a log of the last 5 things a user viewed, and present that list to them on a section of the page called "Recently viewed items" for convenience.
I also have a separate table called "admin_actions" that I use for security logging that keeps track of everything done under an admin account, and what admin account made what sort of change.
I guess the answer is that it depends on how many users there are. If it's not a small, defined number then I'd suggest that it's not a good idea to create one table per user.
I'd suggest a single table with one column being a unique identifier for the user. Make sure that whenever you're querying the table that you're using an index that has this column as the first column in the key. E.g. PRIMARY KEY(user_id, activity_time)
This should allow for fast and efficient reading of the rows.

Alter MediaWiki's user table

How can I change the default MySQL table "user" , that's used to store all members?
is it more of a database question? in mysql you could issue command, for example:
ALTER TABLE user ADD COLUMN user_birthday DATE;
check the actual name of user table. MW uses table prefix option which may not be empty.
The actual statement will depend on what column you want to add of course.
However, for the better portability of your wiki (e.g. easier upgrade) you might want to create a new table instead for the user profile that would have user_id as FOREIGN_KEY
Also there is a field user_options which stores name=value pairs of extra data. - you can make use of that if you don't care about searching your DB against the new "field".
Do not.
If you need to alter the user table (and related) server-side, use the appropriate maintenance script: createAndPromote.php is among the most used, there are others for specific functions.
You didn't specify your use case, but chances are that you're going to break MediaWiki in unexpected ways if you try to manually alter the table.
Here is some information about the "user" table.