Where should you store a lot of custom Strings in RoR? - mysql

I have a portal model. Every user gets their own portal. Now the user can customize various strings across multiple pages on that portal to show their customers. Overall there are around 50 strings and an average user changes from defaults to around 7 of them.
One way to go about is to use a table per page and push strings for a page to that table as columns and map those tables to the portal. However, this would create 5 additional tables, models and corresponding management of forms. Also, these would be very sparse tables.
Is there a better way for this?

Why have a separate table for each page? It's very likely that terms will be shared across pages and when you add a new page to the app you will then have to add a new table??!!
My preference would just to have one table with the 'term' being the value used in your html and the other being the user preference
table user_strings (
user_id int not null
term varchar not null
val varchar not null
primary key(user_id, term)
foreign key (user_id) references user(id) on delete cascade
index (term)
)
If you are not using composite primary keys, then add a the default id

Related

Should I be using onDelete=cascade with my foreign keys?

Related question: Foreign key constraints: When to use ON UPDATE and ON DELETE.
We'll take an example, a company table with a user table containing people from theses company
CREATE TABLE COMPANY (
company_id INT NOT NULL,
company_name VARCHAR(50),
PRIMARY KEY (company_id)
) ENGINE=INNODB;
CREATE TABLE USER (
user_id INT,
user_name VARCHAR(50),
company_id INT,
INDEX company_id_idx (company_id),
FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;
ON DELETE CASCADE : dangerous : if you delete a company row in table COMPANY the engine will delete as well the related USERs. This is dangerous but can be used to make automatic cleanups on secondary tables (so it can be something you want, but quite certainly not for a COMPANY<->USER example)
Now, let's suppose that I have multiple companies, each with multiple customers. I make a habit of having a primary auto index key on each table, and using that as a foreign key on child tables.
So, since my company_id is auto generated and guaranteed to be unique, is there any danger in me setting the foreign key company_id in the users table to onDelete=cascade?
Obviously, my GUI has lots of "are you sure that you are certain that you really want to delete this? Action cannot be undone!"
But, if I don't onDelete=cascade, then before I can DELETE FROM companies WHERE company_id=X, I first have to DELETE FROM users WHERE company_id=X, which is what I have been doing until now.
I am considering onDelete=cascade for the first time & just want to be sure that I have grokked it. Deleting dependent rows can get tedious when the dependency tree is multiple levels deep.
Also, since the keys are auto index, they won;t change, so I can't see that I would need onUpdate.
[Update] One answer was concerned about deleting business data. That's just an example from a related question.
Imagine architecture: a single user can have plans of multiple sites, each with multiple buildings, each with multiple floors, each with multiple rooms.
It is a cascading, tree-like, hierarchy. Does it make sense to have onDelete=Cascade there? I think so, but want to hear from those more more knowledgeable
So much of it will depend on your specific use case. Since you are trying to delete the users anyway, and you want it to happen automatically as part of the cleanup it seems like a good candidate for using ON DELETE to me.
I probably wouldn't be deleting these records though. I would be deactivating them, setting the company to inactive. Then ON UPDATE would be a good candidate, cascading the inactive state down to all users for the company.
I would hesitate to do the delete for two reasons:
First, if the company returns, this allows you to restore the pieces you want for faster setup. And less likely to trigger a restore-from-backup if a company is incorrectly deleted.
Second, I assume that the these entities propagate out into other tables too. I wouldn't want to delete a client/suppliers order history just because we no longer have an active relationship. Even if you don't delete records from those other tables, you'll wind up orphaning the companyId/userId likely in those records.

User table design seperating password related columns

I'm designing an application and I need to create an user registration system. I have the following table structure
Where it doesn't click for me is that should I separate all password related columns onto another table as PASSWORD and connect this to main user table with a foreign key. Having said that, currently passwords are derived with a key derivation algorithm meaning two passwords wouldn't yield the same output digest. However, I wonder if having the user table like this or with a foreign key connecting to the password related columns would increase the performance by any means?
You seem to have an interest in historical passwords. That suggests that you have the wrong data model. It sounds like you want a type-2 table -- one that keeps track of passwords over time:
create table user_passwords (
user_password_id int auto_increment primary key,
user_id int not null,
password varchar(100),
eff_date datetime not null,
end_date datetime,
constraint fk_user_passwords_user_id (user_id) references users(user_id)
);
When a user changes the password, you would then insert a new row into this table, adjusting the eff_date and end_dates.
Note: The purpose of doing this is not for performance. The purpose is to accurately represent the data that you seem to need for your application.
This doesn't include the "trials". I'm not sure what that really means and it probably doesn't need to be kept historically, so that can stay in the users table.

Is it possible to dynamically add SET values to a table?

I have a number of tables in which I need to reference scene IDs, which could be a SET. The problem is that I need to be able to update a set in a table that contains my login information for the app. This set needs to expand(or potentially shrink) based on the number of scenes that exist on the DB. Is it possible to do in phpmyadmin?
From what I've seen in the web interface, I must pre-define the SET values. But I cannot find any info on how to edit the SET's possible values after the column has been created.
What you have is a many-to-many relationship between logins and scenes.
The correct way to implement this is with three tables, for example:
CREATE TABLE logins (login_id INT PRIMARY KEY ...);
CREATE TABLE scenes (scene_id INT PRIMARY KEY ...);
CREATE TABLE login_has_scene (
login_id INT NOT NULL,
scene_id INT NOT NULL,
PRIMARY KEY (login_id, scene_id),
FOREIGN KEY (login_id) REFERENCES logins (login_id),
FOREIGN KEY (scene_id) REFERENCES logins (scene_id)
);
This way you can add new scenes anytime, and you can reference any scene from any login by adding one row per login-scene pair.
This is better than using a SET because SET requires you to redefine the list of values using ALTER TABLE every time you add a scene, and this will become quite a chore.
Also a SET column only allows up to 64 distinct values. If you ever want these tables to support further scenes, you'd have to add more SET columns, or start recycling scene ids or something.
The many-to-many table is a much better solution.
Frankly, I have been using MySQL for nearly 20 years, and I've never found a good use for the SET data type.

What is the appropriate data type for a course_code?

Okay, I am asked to prepare a university database and I am required to store certain data in certain way.
For example, I need to store a course code that has a letter and followed by two integers. eg. I45,D61,etc.
So it should be VARCHAR(3) am I right? But I am still unsure whether this is the right path for it. I am also unsure how I am going to enforce this in the SQL script too.
I can't seem to find any answer for it in my notes and I am currently writing the data dictionary for this question before I meddle into the script.
Any tips?
As much as possible, make primary key with no business meaning. You can easily change your database design without dearly affecting the application layer side. With dumb primary key, the users don't associate meaning to the identifier of a certain record.
What you are inquiring about is termed as intelligent key, which most often is user-visible. The non user-visible keys is called dumb or surrogate key, sometimes this non user-visible key become visible, but it's not a problem as most dumb key aren't interpreted by the user. An example, however you want to change the title of this question, the id of this question will remain the same https://stackoverflow.com/questions/10412621/
With intelligent primary key, sometimes for aesthetic reasons, users want to dictate how the key should be formatted and look like. And this could get easily get updated often as often as users feel. And that will be a problem on application side, as this entails cascading the changes on related tables; and the database side too, as cascaded updating of keys on related tables is time-consuming
Read details here:
http://www.bcarter.com/intsurr1.htm
Advantages of surrogate keys: http://en.wikipedia.org/wiki/Surrogate_key
You can implement natural keys(aka intelligent key) alongside the surrogate key(aka dumb key)
-- Postgresql has text type, it's a character type that doesn't need length,
-- it can be upto 1 GB
-- On Sql Server use varchar(max), this is upto 2 GB
create table course
(
course_id serial primary key, -- surrogate key, aka dumb key
course_code text unique, -- natural key. what's seen by users e.g. 'D61'
course_name text unique, -- e.g. 'Database Structure'
date_offered date
);
The advantage of that approach is when some point in the future the school expand, then they decided to offer an Spanish language-catered Database Structure, your database is insulated from the user-interpreted values that are introduced by the user.
Let's say your database started using intelligent key :
create table course
(
course_code primary key, -- natural key. what's seen by users e.g. 'D61'
course_name text unique, -- e.g. 'Database Structure'
date_offered date
);
Then came the Spanish language-catered Database Structure course. If the user introduce their own rules to your system, they might be tempted to input this on course_code value:
D61/ESP, others will do it as ESP-D61, ESP:D61. Things could get out of control if the user decided their own rules on primary keys, then later they will tell you to query the data based on the arbitrary rules they created on the format of the primary key, e.g. "List me all the Spanish language courses we offer in this school", epic requirement isn't it? So what's a good developer will do to fit those changes to the database design? He/she will formalize the data structure, one will re-design the table to this:
create table course
(
course_code text, -- primary key
course_language text, -- primary key
course_name text unique,
date_offered date,
constraint pk_course primary key(course_code, course_language)
);
Did you see the problem with that? That shall incur downtime, as you needed to propagate the changes to the foreign keys of the table(s) that depends on that course table. Which of course you also need first to adjust those dependent tables. See the trouble it could cause not only for the DBA, and also for the dev too.
If you started with dumb primary key from the get-go, even if the user introduce rules to the system without your knowing, this won't entail any massive data changes nor data schema changes to your database design. And this can buy you time to adjust your application accordingly. Whereas if you put intelligence in your primary key, user requirement such as above can make your primary key devolve naturally to composite primary key. And that is hard not only on database design re-structuring and massive updating of data, it will be also hard for you to quickly adapt your application to the new database design.
create table course
(
course_id serial primary key,
course_code text unique, -- natural key. what's seen by users e.g. 'D61'
course_name text unique, -- e.g. 'Database Structure'
date_offered date
);
So with surrogate key, even if users stash new rules or information to the course_code, you can safely introduce changes to your table without compelling you to quickly adapt your application to the new design. Your application can still continue and won't necessitate downtime. It can really buy you time to adjust your app accordingly, anytime. This would be the changes to the language-specific courses:
create table course
(
course_id serial primary key,
course_code text, -- natural key. what's seen by users e.g. 'D61'
course_language text, -- natural key. what's seen by users e.g. 'SPANISH'
course_name text unique, -- e.g. 'Database Structure in Spanish'
date_offered date,
constraint uk_course unique key(course_code, course_language)
);
As you can see, you can still perform a massive UPDATE statement to split the user-imposed rules on course_code to two fields which doesn't necessitate changes on the dependent tables. If you use intelligent composite primary key, restructuring your data will compel you to cascade the changes on composite primary keys to dependent tables' composite foreign keys. With dumb primary key, your application shall still operate as usual, you can amend changes to your app based on the new design (e.g. new textbox, for course language) later on, any time. With dumb primary key, the dependent table doesn't need a composite foreign key to point to the course table, they can still use the same old dumb/surrogate primary key
And also with dumb primary key, the size of your primary key and foreign keys won't expand
This is the domain solution. Still not perfect, check can be improved, etc.
set search_path='tmp';
DROP DOMAIN coursename CASCADE;
CREATE DOMAIN coursename AS varchar NOT NULL
CHECK (length(value) > 0
AND SUBSTR(value,1) >= 'A' AND SUBSTR(value,1) <= 'Z'
AND SUBSTR(value,2) >= '0' AND SUBSTR(value,2) <= '9' )
;
DROP TABLE course CASCADE;
CREATE TABLE course
( cname coursename PRIMARY KEY
, ztext varchar
, UNIQUE (ztext)
);
INSERT INTO course(cname,ztext)
VALUES ('A11', 'A 11' ), ('B12', 'B 12' ); -- Ok
INSERT INTO course(cname,ztext)
VALUES ('3','Three' ), ('198', 'Butter' ); -- Will fail
BTW: For the "actual" PK, I would probably use a surrogate ID. But the domain above (with UNIQUE constraint) could serve as a "logical" candidate key.
That is basically the result of the Table is Domain paradigm.
I strongly recommend you not get too specific about the datatype, so something like VARCHAR(8) would be fine. The reasons are:
Next year there might be four characters in the code. Business needs change all the time, so don't lock down field lengths too much
Let the application layer handle validation - after all, it has to communicate the validation problem to the user
You're adding little or no business value by limiting it to 3 chars
With mysql, although you can define check constraints on columns (in the hope of "validating" the values), they are ignored and are allowed for compatibility reasons only
Of all the components of your system, the database schema is always the hardest thing to change, so allow some flexibility in your data types to avoid changes as much as possible.

Deal with table that's composite primary key I wanted to include a nullable column

Assume a table that may look like this:
userId INT (foreign key to a users table)
profileId INT (foreign key to a profiles table)
value INT
Say that in this table preferences for users are saved. The preference should be loaded according to the current user and the profile that the current user has selected. That means that the combination of userId and profileId is unique and can be used as a composite primary key.
But then I want to add the ability to also save a default value that should be used if no value for a specific profileId is save in the database. My first idea would be to set the profileId column to nullable and say that the row that has null as profileId contains the default value. But then I can't use a composite primary key that involves this table, because nullable columns can't be part of a primary key.
So what's the "best" way to work around this? Just drop the primary key completely and go without primary key? Generate an identity column as primary key that I never need? Create a dummy profile to link to in the profile table? Create a separate table for default values (which is the only option that guarantees that no userId has multiple default values??)?
Update: I thought about Dmitry's answer but after all it has the drawback that I can't even create a unique constraint on the two columns userId and profileId (MySQL will allow duplicate values if profileId is null and DB2 will refuse to even create a unique constraint on a nullable column). So with Dmitry's solution I will have to live without this consistency check of the DB. Is that acceptable? Or is that not acceptable (after all consistency checks are a major feature of relational DBs). What is your reasoning?
Create ID autoincrement field for your primary key.
AND
Create unique index for (userId, profileId) pair. If necessary create dummy profile instead of null.
Dmitry's answer is a good one, but since your case involves what is essentially an intersection table, there is another good way to solve this. For your situation I also like the idea of creating a default user profile that you can use in your code to establish default settings. This is good because it keeps your data model clean without introducing extra candidate keys. You would need to be clear in this dummy/default profile that this is what it is. You can give it a clear name like "Default User" and make sure that nobody but the administrator has access to the user credentials.
One other advantage of this solution is that you can sign on as the default user and use your system's GUI to modify the defaults rather than having to fiddle with the data through DB access tools. Depending on the policies in your shop, direct access to the data tables by programmers may be hard or impossible. Using the tested/approved GUIs for modifying defaults removes a lot of red tape and prevents some kinds of accidental damage to the data.
Bottom Line: Primary keys are important. In a transactional system every table should have a at least one unique index one of which should be the primary key. You can always enforce this by adding a surrogate (auto increment) key to every table. Even if you do, you still generally want a natural unique index whenever possible. This is how you will generally find what you're looking for in a table.
Creating a Default User entry in your user table isn't a cheat or a hack, it's using your table structure the way it's meant to be used and it allows you to put a usable unique contraint on the combination of user ID and profile ID, regardless of whether you invent an additional, arbitrary unique constraint with a surrogate key.
This is the normal behaviour of UNIQUE constrain on a NULL column. It allows one row of data with NULL values. However, that is not the behaviour we want for this column. We want the column to accept unique values and also accept multiple NULL values.
This can be achieved using a computed column and adding a contraint to the computed column instead default null value.
Refer below article will help you more in this matter:
UNIQUE Column with multiple NULL values
I always always always use a primary auto_increment key on a table, even if its redundant; it just gives me a fantastically simple way to identify a record I want to access later or refer to elsewhere. I know it doesn't directly answer your question, but it does make the primary key situation simpler.
create table UserProfile ( int UserProfileID auto_increment primary key etc.,
UserID int not null, ProfileID int );
Then create a secondary index UserProfileIDX(UserID, ProfileID) that's unique, but not the primary key.