Rails table association with ENUM column - mysql

Given the table uploads which holds the relation between 4 different apps and users:
field type
dogTag int (foreign key to dvd)
app enum
uploader int (foreign key to user)
mod string
...
And the table dvds:
dogTag int (primary key)
title string
...
And the table users:
id int (primary key)
...
How can I properly construct a model relations between the dvds table and the uploads table within Rails if it depends on an ENUM column?
With sql I simply do:
JOIN uploads ON uploads.dogTag = dvds.dogTag
WHERE uploads.app = 'dvd'
But have no idea how to create this relationship in Rails and haven't found a lot of info on this.
Thanks

I don't have too much idea on how to create relation with enum column,but if it's a four model in your app that has one upload model then you can use polymorphic association,ryan has great railscasts

Well you could re-organise your db to make the enum type a look up table, you could create a non-persistent model to implement it.
Thing is if you had started from the model instead of the database, you wouldn't have gone anywhere near mysql's enum type, and that's why you are struggling to find much, you've gone at it bass ackwards.
My advice get rid of it...

Related

What's the meaning of foreignKey in Sequelize.hasMany/belongsTo?

I was recently using Sequelize the ORM.
I have two tables. One is Users and the other is Posts
The schema of these two tables are as follows
Users {
id: Integer,
name: String,
age: Integer
}
Posts {
id: Integer, // refers to the id of post itself
author_id: Integer, // refers to the id of the author of this post
title: String,
content: String
}
I want to create an one(Users)-to-many(Posts) association between them. In order to do that, I need to specify the hasMany & belongsTo in the models.
However, I am very confused about the meaning of the parameters foreignKey / sourceKey / targetKey.
Say that I already define and create my table with migrations. The name of the attribute which is the foreignKey is author_id in this case.
My guess is, in belongsTo, foreignKey means "the name of the attribute that is going to be foreignKey in the source table"?
But in hasMany, foreignKey means "the name of the attribute that is referenced by the coming foreignKey"
So, foreignKey in belongsTo will be author_id (in table Posts) but foreignKey in hasMany will be id (in table Users) ?
Furthermore, what on earth do the sourceKey/targetKey mean!?
Well... you have an interesting case above... if you used user_id instead of author_id then you might get away without declaring these as sequelize might assume them correctly... However because you named it author_id then your hasMany definitely needs to know that the foreign key is named author_id in your post table... Let's say that you named your id in the user table something like "user" instead of "id".... well sequelize won't understand that and be able to infer what it is joining on, so you would say the the sourceKey = "user" and foreignKey = "author_id"... To further that one more you may run into issues in database design where you need to specify what the targetKey or otherKey is because someone was naming things whacky... so like you said above in your belongsTo author_id would have no clue that it was supposed to map back to "user" for it's join, so you would specify foreignKey = "author_id" and otherKey = "user"... This stuff took me a while to wrap my head around as well because i did not get to design the database i implements graphql/sequelize against... Therefore i had to make wide use of sourceKey, targetKey, and otherKey etc etc in my joins..
Try and think of it really logically and it will make more sense... if your primary key is always "id" and your foreignKey is always "tablename_id" then you won't need to worry too much about the other properties.. but when naming doesn't line up, sequelize needs to be told what keys to use, and that is why those other properties exist.. Sequelize is damn smart, but it can't make up for poor db design or bad join column naming... Hope this helps, if not i have plenty of examples i can post for you.. Cheers

Mapping values from foreign keys in table

I'm using hibernate for my project and what i'm essentially trying to do is figure out how to auto-map the values with foreign keys from my table in the database to a data object.
For example, I have a users table with the following columns
id - INT
username - VARCHAR
password - VARCHAR
email - VARCHAR
firstName - VARCHAR
lastName - VARCHAR
This is fairly straightforward to map as there are no foreign keys involved. The code I have is:
SQLQuery q = session.createSQLQuery("SELECT * FROM users WHERE username=? AND password=?");
q.setString(0, username);
q.setString(1, password);
q.addEntity(User.class);
List<User> users = q.list()
Now supposing I add some foreign keys to my user table such as
userlevel_id - INT
department_id - INT
Which reference the user level a user belongs to and the department. How do I get hibernate to map the user level name from the userlevel table and the department name from the department table? It wont be of much use if I just store the id's in the User data object as I will need to display the values to my views later on. Any help will be greatly appreciated, Thanks!!
Use an eagerly loaded #ManyToOne mapping on your User object. Make an object to represent both your Departmentand your UserLevel and add them as fields into your User object. The reason that you should have this mapping as eagerly loaded is that there is never a situation where you don't want to load a User without their Department or UserLevel. Hibernate will automatically map your User object to the appropriate Department and UserLevel.
If you want too, you can make the relationship bi-directional so you can get all users in a department by selecting a department. Anything you can do in SQL you can map using Hibernate.
Have a read of the documentation and see if that helps.

Normalizing MySQL data

I'm new to MySQL, and just learned about the importance of data normalization. My database has a simple structure:
I have 1 table called users with fields:
userName (string)
userEmail (string)
password (string)
requests (an array of dictionaries in JSON string format)
data (another array of dictionaries in JSON string format)
deviceID (string)
Right now, this is my structure. Being very new to MySQL, I'm really not seeing why my above structure is a bad idea? Why would I need to normalize this and make separate tables? That's the first question-why? (Some have also said not to put JSON in my table. Why or why not?)
The second question is how? With the above structure, how many tables should I have, and what would be in each table?
Edit:
So maybe normalization is not absolutely necessary here, but maybe there's a better way to implement my data field? The data field is an array of dictionaries: each dictionary is just a note item with a few keys (title, author, date, body). So what I do now is, which I think might be inefficient, every time a user composes a new note, I send that note from my app to PHP to handle. I get the JSON array of dictionaries already part of that user's data, I convert it to a PHP array, I then add to the end of this array the new note, convert the whole thing back to JSON, and put it back in the table as an array of dictionaries. And this process is repeated every time a new note is composed. Is there a better way to do this? Maybe a user's data should be a table, with each row being a note-but I'm not really sure how this would work?
The answer to all your questions really depends on what the JSON data is for, and whether you'll ever need to use some property of that data to determine which rows are returned.
If your data truly has no schema, and you're really just using it to store data that will be used by an application that knows how to retrieve the correct row by some other criteria (such as one of the other fields) every time, there's no reason to store it as anything other than exactly as that application expects it (in this case, JSON).
If the JSON data DOES contain some structure that is the same for all entries, and if it's useful to query this data directly from the database, you would want to create one or more tables (or maybe just some more fields) to hold this data.
As a practical example of this, if the data fields contains JSON enumerating services for that user in an array, and each service has a unique id, type, and price, you might want a separate table with the following fields (using your own naming conventions):
serviceId (integer)
userName (string)
serviceType (string)
servicePrice (float)
And each service for that user would get it's own entry. You could then query for users than have a particular service, which depending on your needs, could be very useful. In addition to easy querying, indexing certain fields of the separate tables can also make for very QUICK queries.
Update: Based on your explanation of the data stored, and the way you use it, you probably do want it normalized. Something like the following:
# user table
userId (integer, auto-incrementing)
userName (string)
userEmail (string)
password (string)
deviceID (string)
# note table
noteId (integer, auto-incrementing)
userId (integer, matches user.userId)
noteTime (datetime)
noteData (string, possibly split into separate fields depending on content, such as subject, etC)
# request table
requestId (integer, auto-incrementing)
userId (integer, matches user.userId)
requestTime (datetime)
requestData (string, again split as needed)
You could then query like so:
# Get a user
SELECT * FROM user WHERE userId = '123';
SELECT * FROM user WHERE userNAme = 'foo';
# Get all requests for a user
SELECT * FROM request WHERE userId = '123';
# Get a single request
SELECT * FROM request WHERE requestId = '325325';
# Get all notes for a user
SELECT * FROM note WHERE userId = '123';
# Get all notes from last week
SELECT * FROM note WHERE userId = '123' AND noteTime > CURDATE() - INTERVAL 1 WEEK;
# Add a note to user 123
INSERT INTO note (noteId, userId, noteData) VALUES (null, 123, 'This is a note');
Notice how much more you can do with normalized data, and how easy it is? It's trivial to locate, update, append, or delete any specific component.
Normalization is a philosophy. Some people think it fits their database approach, some don't. Many modern database solutions even focus on denormalization to improve speeds.
Normalization often doesn't improve speed. However, it greatly improves the simplicity of accessing and writing data. For example, if you wanted to add a request, you would have to write a completely new JSON field. If it was normalized, you could simply add a row to a table.
In normalization, "array of dictionaries in JSON string format" is always bad. Array of dictionaries can be translated as list of rows, which is a table.
If you're new to databases: NORMALIZE. Denormalization is something for professionals.
A main benefit of normalization is to eliminate redundant data, but since each user's data is unique to that user, there is no benefit to splitting this table and normalizing. Furthermore, since the front-end will employ the dictionaries as JSON objects anyway, undue complication and a decrease in performance would result from trying to decompose this data.
Okay, here is a normalized mySQL data-model. Note: you can separate authors and titles into two tables to further reduce data redundancy. You can probably use similar techniques for the "requests dictionaries":
CREATE TABLE USERS(
UID int NOT NULL AUTO_INCREMENT PRIMARY KEY,
userName varchar(255) UNIQUE,
password varchar(30),
userEmail varchar(255) UNIQUE,
deviceID varchar(255)
) ENGINE=InnoDB;
CREATE TABLE BOOKS(
BKID int NOT NULL AUTO_INCREMENT PRIMARY KEY,
FKUSERS int,
Title varchar(255),
Author varchar(50)
) ENGINE=InnoDB;
ALTER TABLE BOOKS
ADD FOREIGN KEY (FKUSERS)
REFERENCES USERS(UID);
CREATE TABLE NOTES(
ID int NOT NULL AUTO_INCREMENT PRIMARY KEY,
FKUSERS int,
FKBOOKS int,
Date date,
Notes text
) ENGINE=InnoDB;
ALTER TABLE NOTES
ADD FOREIGN KEY BKNO (FKUSERS)
REFERENCES USERS(UID);
ALTER TABLE NOTES
ADD FOREIGN KEY (FKBOOKS)
REFERENCES BOOKS(BKID);
In your case, I will abstract out the class that handles this table. Then keep the data normalized. if in future, the data access patterns changes and i need to normalized the data, i css just do so with less impact on the program. I just need to change the class that handles this set of data to query the normalized tables , but return the data as if the database structure never changed.

Enum datatype versus table of data in MySQL?

I have one MySQL table, users, with the following columns:
user_id (PK)
email
name
password
To manage a roles system, would there be a downside to either of the following options?
Option 1:
Create a second table called roles with three columns: role_id (Primary key), name, and description, then associate users.user_id with roles.role_id as foreign keys in a third table called users_roles?
Or...
Option 2:
Create a second table called roles with two columns: user_id (Foreign key from users.user_id) and role (ENUM)? The ENUM datatype column would allow for a short list of allowable roles to be inserted as values.
I've never used the ENUM datatype in MySQL before, so I'm just curious, as option 2 would mean one less table. I hope that makes sense, this is the first time I've attempted to describe MySQL tables in a forum.
In general, ENUM types are not meant to be used in these situations. This is especially the case if you intend to cater for the flexibility of adding or removing roles in the future. The only way to change the values of an ENUM is with an ALTER TABLE, while defining the roles in their own table will simply require a new row in the roles table.
In addition, using the roles table allows you to add additional columns to better define the role, like the description field you suggested in Option 1. This is not possible if you were to use an ENUM type as in Option 2.
Personally I would not opt for an ENUM in these scenarios. Maybe I can see them being used for columns with an absolutely finite set of values, such as {Spades, Hearts, Diamonds, Clubs} to define the suit of a card, but not in cases such as the one in question, for the disadvantages mentioned earlier.
Using ENUM for the case You suggested only makes sense when You have a strictly definded ORM on the receiving end that for istance maps db rows into a list of flat objects automatically.
Example:
table animal( ENUM('reptiles','mamals') Category, (varchar 50)Name );
is automatically maped to
object animal
animal->Category
animal->Name

Database Design w/ Foreign Keys Question

I'm trying to use foreign keys properly to maintain data integrity. I'm not really a database guy so I'm wondering if there is some general design principle I don't know about. Here's an example of what I'm trying to do:
Say you want to build a database of vehicles with Type (car, truck, etc.), Make, and Model. A user has to input at least the Type, but the Make and Model are optional (if Model is given, then Make is required). My first idea is to set up the database as such:
Type:
-id (PK)
-description
Make:
-id (PK)
-type_id (FK references Type:id)
-description
Model:
-id (PK)
-make_id (FK references Make:id)
-description
Vechicle:
-id (PK)
-type_id (FK references Type:id)
-make_id (FK references Make:id)
-model_id (FK references Model:id)
How would you setup the FKs for Vehicle to ensure that the Type, Make, and Model all match up? For example, how would you prevent a vehicle having (Type:Motorcyle, Make:Ford, Model:Civic)? Each of those would be valid FKs, but they don't maintain the relationships shown through the other tables' FKs.
Also, because Model isn't required, I can't just store the model_id FK and work backwards from it.
I'm not tied to the database design at all, so I'm open to the possibility of having to change the way the tables are set up. Any ideas?
P.S. - I'm using mysql if anyone's interested, but this is more of a general question about databases.
Edit (Clarifications):
-type_id and make_id are needed in the vehicle table unless there is some way to figure those out in the case that model_id is null;
-the relationships between type_id, make_id, and model_id need to be maintained.
What you are looking for is a CHECK constraint. Unfortunately MySQL does not currently support this. You could emulate such functionality with triggers but you would need to create both an INSERT and an UPDATE trigger for it to work.
However, as other answers have indicated, all you should really be storing is the vehicle model. In you application you should be drilling down to the type if it's available.
Like this:
Type:
id (PK)
description
Make:
id (PK)
type_id (FK references Type:id, not null)
description
Model:
id (PK)
make_id (FK references Make:id, not null)
description
Vechicle:
id (PK)
model_id (FK references Model:id)
Basically don't double reference make and type from vehicle as well. You'll run into problems if you do that. You can get the make and type from the model of the vehicle (if defined). Model must have make. Make must have type.
Think about that for a second: if vehicle has a given model but vehicle and model both have a make, those values can be different. This kind of inconsistency can develop because of information redundancy. You want to avoid that generally.
If you need to figure out the make and type of a vehicle the SQL starts to look like this:
SELECT v.id, v.model_id, m.make_id, k.type_id
FROM vehicle v
LEFT JOIN model m ON v.model_id = m.id
JOIN make k ON m.make_id = k.id
JOIN type t ON k.type_id = t.id
And so on.
Here is one approach:
- One make (Ford, GM, Honda) can have many models, one model belongs to only one make.
- Model is of a certain type (car, truck bike).
- Vehicle is of a certain model. One vehicle can be of only one model; there can be many vehicles of a model.
Model table contains columns common to all models; while car, truck, and motorcycle have columns specific to each one.
When modeling a DB, consider data, entities and relationships; don't start from the UI -- there is a business layer in between to sort things out. It is OK to use MySQL, you can enforce check and foreign key constraints on your application layer.
Your design is fine for data integrity, it will be the job of your application to maintain that a Vehicle must be made up of Makes from a particular Type and Models of a particular Make.
If you want to maintain vehicle type/make/model integrity in the database you could add a check constraint to your Vehicle table that makes sure the Vehicle's make's type id equals the provided type id. And if the model id is not null, make sure it's make id is the same as the make id provided.
I see you already accepted an answer, but an alternate approach that handles your actual structural problem and doesn't use triggers or check constraints would be to create dummy entries in the Make and Model tables with a description of "n/a" or such, one for each entry in Type and Make respectively, and then get rid of the redundant columns in Vehicle.
That way, if all you know is the Type of a vehicle, you'd find the dummy entry in Make that references the appropriate Type, then find the dummy entry in Model that references that Make, then reference that Model from the new row in Vehicle.
The main downsides of course would be extra housekeeping to create the dummy rows, either ahead of time when adding a Type or Make, or on demand when adding a Vehicle with missing data.