I am building a commenting system where people can comment on uploaded files, messages and to-do items. What is the best way to connect the comment table table to the other various tables?
Possible Solutions
Solution one - use a two field foreign key.
CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
foreign_key INT NOT NULL,
table_name enum('files','messages','to-do'),
user_id INT NOT NULL,
comment TEXT NOT NULL);
Solution two - Each table would have a primary key unique to the database. So I would use php's uniqid($prefix) as the primary keys for each table.
CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
foreign_key char(23) NOT NULL,
table_name enum('files','messages','to-do'),
user_id INT NOT NULL,
comment TEXT NOT NULL);
Solution Three - Have multiple foreign keys in the comment table
CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
files_id INT NOT NULL,
messages_id INT NOT NULL,
to_do_id INT NOT NULL,
user_id INT NOT NULL,
comment TEXT NOT NULL);
What is the best solution? I appreciate your input and please let me know if I can clarify anything
EDIT removed table_name from solution three as it was a copy_paste error
As to Joe's Response
Assume:
1) all data is already escaped. Do we really need to see that?
2) $fileId = "146".
3) $userId = "432".
4) $comment = "Stackoverflow is so awesome!"
INSERT
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) {
die('Could not connect: ' . mysql_error());
}
mysql_select_db('mydb');
mysql_query("INSERT INTO `comments` (user_id,comment) VALUES($userId,$comment)");
$commentId = mysql_insert_id();
mysql_query("INSERT INTO `comments_files_xref` (file_id,comment_id) VALUES($fileId,$commentId)");
Personally, I would normalize the design a bit more. Perhaps something like:
Multiple remarks :
You shouldn't call your foreign key foreign_key because a foreign key is a constraint, not a field in a way. it references a field in a table to an index on another, call it the same way you called the PK on the table you reference, or something recognizable.
Foreign keys constraints only works on innodb, if you use MyISAM forget about them and do a lot of checks with PHP.
read http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html
You should make a sketch of what you want in your DB or use tools like mysql workbench (which is free) that helps seeing the schema better.
As I see your problem and if you want to use constraint here, I'll use solution one or another solution :
1-
CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, // Which is your comment index
idTable INT NOT NULL, // ID of the message
table_name enum('files','messages','to-do'), // which it comes from
user_id INT NOT NULL, // etc...
comment TEXT NOT NULL);
But there are conditions :
The PK of files, messages _to-do_ must have the same format (INT)
If you want to add a module (to files, messages _to-do_) it'll be difficult
2-
Create tables joining comments and other tables :
CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, // Which is your comment index
user_id INT NOT NULL, // etc...
comment TEXT NOT NULL);
CREATE TABLE `comments-files`(
id_comments INT NOT NULL PRIMARY KEY,
id_files INT NOT NULL PRIMARY KEY);
etc. Hope you see the point here. You add constraint thanks to http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html if needed.
I just learn Ruby on Rails in my current company, and solution 1 is preferred because RoR's Active Record can handle it as polymorphic relation.
Back to the topic, that you are using PHP, I prefer either solution 1 or 3. Solution 1 is preferable if there are possibilities that the comment table will be used for other table in the future.
One note, in solution 3, I think the table_name column is not needed. You can determine for which table the comment is by fill either files_id, messages_id, or to_do_id with the id, then set 2 other foreign key with 0.
I would create a join table for each thing that can be commented on, so an files_comments table and a todo_comments table. But solution 1 would be an alternative. I would avoid solutions two or three... could get messy if things change in the future.
Related
First of all, I'm from Spain so I'm sorry if I made some mistakes writing. So, I have two problems. It will be better if I give context before. I am not even junior, still learning code, and I thought that it will be a good proyect to create a web page where you can add ingredients, foods with that ingredients, etc. So I decided to start learning PHP and SQL. Now I'm trying to create a database, starting with some ingredients and two kinds of rices. My 1st problem is that I don't know if I need to create a data base for that. The second and main one is that I don't have any idea about how to get this working as I want.
See, First of all I created the table for ingredients´
CREATE TABLE ingredientes(
id int(255) auto_increment not null,
ingrediente varchar(255) not null,
CONSTRAINT pk_ingredientes PRIMARY KEY(id) )ENGINE=InnoDb;
Sorry 'cause it's on spanish :/, but nothing to hard to understand.
So I add some ingredients.
Here the pic showing them
After that I created two tables, and add ingredients to them.
CREATE TABLE arroz_con_pollo(
id int(255) auto_increment not null,
ingrediente int(255) not null,
CONSTRAINT pk_arroz_con_pollo PRIMARY KEY(id),
CONSTRAINT fk_pollo_ingredientes FOREIGN KEY(ingrediente) REFERENCES ingredientes(id) )ENGINE=InnoDb;
CREATE TABLE arroz_cubana(
id int(255) auto_increment not null,
ingrediente int(255) not null,
CONSTRAINT pk_arroz_cubana PRIMARY KEY(id),
CONSTRAINT fk_cubana_ingredientes FOREIGN KEY(ingrediente) REFERENCES ingredientes(id))ENGINE=InnoDb;
Here the picture showing the ID's.
Here
So now I spend a lot of time researching and find out that I can show the names by using this command
SELECT a.id,i.ingrediente
FROM ingredientes i, arroz_cubana a
WHERE i.id = a.id;
And have something like this
At this point, everything is, more or less, working. My issue came when I want to create a data base that keep all the names (arroz con pollo, arroz cubana...) in an only table named as 'rices' to be able to choose a name, and automatically have the ingredients there, without any complication for the user. But, I literally have no idea. I've been coding for hours without any victory on that. And I haven't see anything similar on the web so, if someone tell me how to fix that issue or how to make that idea of a web to keep ingredients and foods, I'll be very greatful.
Your data structure is messed up. SQL is not designed to have a separate table for each ingredient. Instead, you want two other tables.
The first is for dishes:
CREATE TABLE dishes (
dish_id int auto_increment not null,
name varchar(255)
);
You would then insert appropriate rows into this:
INSERT INTO dishes (name)
VALUES ('arroz_on_pollo');
Then you have another table for the ingredients:
CREATE TABLE dishes_ingredients (
dish_ingredient_id int auto_increment primary key,
dish_id int not null
ingredient_id int not null,
CONSTRAINT fk_dish_ingredientes_dish FOREIGN KEY(dish_id) REFERENCES dishes(dish_id)
CONSTRAINT fk_dish_ingredientes_dish FOREIGN KEY(ingredient_id) REFERENCES ingredientes(ingredient_id)
);
Voila! New dishes are just rows in a table, so you can get the names using a SELECT.
Notes on structure:
int(255) really makes no sense. Just use int. The number in parentheses is a width for the value when printing it and 255 is a ridiculous width.
I am a fan of naming primary keys with the table name. That way, the primary key and foreign key typically have the same name.
You should not have a table per dish. Create one table "dish", that includes a column "name". Each row represents a dish. Then create a supporting table where you list the (multiple) ingredients for each dish. Look around for a tutorial on databases, this topic is too large to explain in a stackoverflow question (or several).
And so you do not need to be able to list the table names, the way you were considering. (Which is not something SQL supports directly; different databases provide non-standard ways to do it, but as explained you do not actually need such a feature.)
I'm using php and i have a table that have 2 column of varchar , one is used for user identification, and the other is used for page name entry.
they both must be varchar.
i want to insert ignore data when user enter a page to know if he visited it or not, and i want to fetch all the rows that the user have been in.
fetch all for first varchar column.
insert if not exist for both values.
I'm hoping to do it in the most efficient way.
what is the best way to insert without checking with another query if exist?
what is the best way other then:
SELECT * FROM table WHERE id = id
to fetch when the column needed is varchar?
You should consider a normalized table structure like this:
CREATE TABLE user (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE page (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE pages_visted (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED,
page_id INT UNSIGNED,
UNIQUE KEY (user_id, page_id)
);
INSERT IGNORE INTO pages_visted (user_id, page_id) VALUES (:userId, :pageId);
SELECT page_id FROM pages_visted WHERE user_id = :userId;
I think you want to implement a composite primary key.
A composite primary key tells MySQL that you want your primary key to be a combination of fields.
More info here: Why use multiple columns as primary keys (composite primary key)
I don't know of a better option for your query, although I can advise, if possible:
Define columns to be NOT NULL. This gives you faster processing and requires less storage. It will also simplify queries sometimes because you don't need to check for NULL as a special case.
And with variable-length rows, you get more fragmentation in tables where you perform many deletes or updates due to the differing sizes of the records. You'll need to run OPTIMIZE TABLE periodically to maintain performance.
I have a table:
table votes (
id,
user,
email,
address,
primary key(id),
);
Now I want to make the columns user, email, address unique (together).
How do I do this in MySql?
Of course the example is just... an example. So please don't worry about the semantics.
To add a unique constraint, you need to use two components:
ALTER TABLE - to change the table schema and,
ADD UNIQUE - to add the unique constraint.
You then can define your new unique key with the format 'name'('column1', 'column2'...)
So for your particular issue, you could use this command:
ALTER TABLE `votes` ADD UNIQUE `unique_index`(`user`, `email`, `address`);
I have a MySQL table:
CREATE TABLE `content_html` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_box_elements` int(11) DEFAULT NULL,
`id_router` int(11) DEFAULT NULL,
`content` mediumtext COLLATE utf8_czech_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `my_uniq_id` (`id_box_elements`,`id_router`)
);
and the UNIQUE KEY works just as expected, it allows multiple NULL rows of id_box_elements and id_router.
I am running MySQL 5.1.42, so probably there was some update on the issue discussed above. Fortunately it works and hopefully it will stay that way.
Multi column unique indexes do not work in MySQL if you have a NULL value in row as MySQL treats NULL as a unique value and at least currently has no logic to work around it in multi-column indexes. Yes the behavior is insane, because it limits a lot of legitimate applications of multi-column indexes, but it is what it is... As of yet, it is a bug that has been stamped with "will not fix" on the MySQL bug-track...
Have you tried this ?
UNIQUE KEY `thekey` (`user`,`email`,`address`)
This works for mysql version 5.5.32
ALTER TABLE `tablename` ADD UNIQUE (`column1` ,`column2`);
MySql 5 or higher behaves like this (I've just tested):
you can define unique constraints involving nullable columns. Say you define a constraint unique (A, B) where A is not nullable but B is
when evaluating such a constraint you can have (A, null) as many times you want (same A value!)
you can only have one (A, not null B) pair
Example:
PRODUCT_NAME, PRODUCT_VERSION
'glass', null
'glass', null
'wine', 1
Now if you try to insert ('wine' 1) again it will report a constraint violation
Hope this helps
You can add multiple-column unique indexes via phpMyAdmin. (I tested in version 4.0.4)
Navigate to the structure page for your target table. Add a unique index to one of the columns. Expand the Indexes list on the bottom of the structure page to see the unique index you just added. Click the edit icon, and in the following dialog you can add additional columns to that unique index.
this tutorial works for me
ALTER TABLE table_name
ADD CONSTRAINT constraint_name UNIQUE (column1, column2, ... column_n);
https://www.mysqltutorial.org/mysql-unique-constraint/
I do it like this:
CREATE UNIQUE INDEX index_name ON TableName (Column1, Column2, Column3);
My convention for a unique index_name is TableName_Column1_Column2_Column3_uindex.
If You are creating table in mysql then use following :
create table package_template_mapping (
mapping_id int(10) not null auto_increment ,
template_id int(10) NOT NULL ,
package_id int(10) NOT NULL ,
remark varchar(100),
primary key (mapping_id) ,
UNIQUE KEY template_fun_id (template_id , package_id)
);
For adding unique index following are required:
1) table_name
2) index_name
3) columns on which you want to add index
ALTER TABLE `tablename`
ADD UNIQUE index-name
(`column1` ,`column2`,`column3`,...,`columnN`);
In your case we can create unique index as follows:
ALTER TABLE `votes`ADD
UNIQUE <votesuniqueindex>;(`user` ,`email`,`address`);
If you want to avoid duplicates in future. Create another column say id2.
UPDATE tablename SET id2 = id;
Now add the unique on two columns:
alter table tablename add unique index(columnname, id2);
First get rid of existing duplicates
delete a from votes as a, votes as b where a.id < b.id
and a.user <=> b.user and a.email <=> b.email
and a.address <=> b.address;
Then add the unique constraint
ALTER TABLE votes ADD UNIQUE unique_index(user, email, address);
Verify the constraint with
SHOW CREATE TABLE votes;
Note that user, email, address will be considered unique if any of them has null value in it.
For PostgreSQL...
It didn't work for me with index; it gave me an error, so I did this:
alter table table_name
add unique(column_name_1,column_name_2);
PostgreSQL gave unique index its own name. I guess you can change the name of index in the options for the table, if it is needed to be changed...
I have a table:
table votes (
id,
user,
email,
address,
primary key(id),
);
Now I want to make the columns user, email, address unique (together).
How do I do this in MySql?
Of course the example is just... an example. So please don't worry about the semantics.
To add a unique constraint, you need to use two components:
ALTER TABLE - to change the table schema and,
ADD UNIQUE - to add the unique constraint.
You then can define your new unique key with the format 'name'('column1', 'column2'...)
So for your particular issue, you could use this command:
ALTER TABLE `votes` ADD UNIQUE `unique_index`(`user`, `email`, `address`);
I have a MySQL table:
CREATE TABLE `content_html` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_box_elements` int(11) DEFAULT NULL,
`id_router` int(11) DEFAULT NULL,
`content` mediumtext COLLATE utf8_czech_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `my_uniq_id` (`id_box_elements`,`id_router`)
);
and the UNIQUE KEY works just as expected, it allows multiple NULL rows of id_box_elements and id_router.
I am running MySQL 5.1.42, so probably there was some update on the issue discussed above. Fortunately it works and hopefully it will stay that way.
Multi column unique indexes do not work in MySQL if you have a NULL value in row as MySQL treats NULL as a unique value and at least currently has no logic to work around it in multi-column indexes. Yes the behavior is insane, because it limits a lot of legitimate applications of multi-column indexes, but it is what it is... As of yet, it is a bug that has been stamped with "will not fix" on the MySQL bug-track...
Have you tried this ?
UNIQUE KEY `thekey` (`user`,`email`,`address`)
This works for mysql version 5.5.32
ALTER TABLE `tablename` ADD UNIQUE (`column1` ,`column2`);
MySql 5 or higher behaves like this (I've just tested):
you can define unique constraints involving nullable columns. Say you define a constraint unique (A, B) where A is not nullable but B is
when evaluating such a constraint you can have (A, null) as many times you want (same A value!)
you can only have one (A, not null B) pair
Example:
PRODUCT_NAME, PRODUCT_VERSION
'glass', null
'glass', null
'wine', 1
Now if you try to insert ('wine' 1) again it will report a constraint violation
Hope this helps
You can add multiple-column unique indexes via phpMyAdmin. (I tested in version 4.0.4)
Navigate to the structure page for your target table. Add a unique index to one of the columns. Expand the Indexes list on the bottom of the structure page to see the unique index you just added. Click the edit icon, and in the following dialog you can add additional columns to that unique index.
this tutorial works for me
ALTER TABLE table_name
ADD CONSTRAINT constraint_name UNIQUE (column1, column2, ... column_n);
https://www.mysqltutorial.org/mysql-unique-constraint/
I do it like this:
CREATE UNIQUE INDEX index_name ON TableName (Column1, Column2, Column3);
My convention for a unique index_name is TableName_Column1_Column2_Column3_uindex.
If You are creating table in mysql then use following :
create table package_template_mapping (
mapping_id int(10) not null auto_increment ,
template_id int(10) NOT NULL ,
package_id int(10) NOT NULL ,
remark varchar(100),
primary key (mapping_id) ,
UNIQUE KEY template_fun_id (template_id , package_id)
);
For adding unique index following are required:
1) table_name
2) index_name
3) columns on which you want to add index
ALTER TABLE `tablename`
ADD UNIQUE index-name
(`column1` ,`column2`,`column3`,...,`columnN`);
In your case we can create unique index as follows:
ALTER TABLE `votes`ADD
UNIQUE <votesuniqueindex>;(`user` ,`email`,`address`);
If you want to avoid duplicates in future. Create another column say id2.
UPDATE tablename SET id2 = id;
Now add the unique on two columns:
alter table tablename add unique index(columnname, id2);
First get rid of existing duplicates
delete a from votes as a, votes as b where a.id < b.id
and a.user <=> b.user and a.email <=> b.email
and a.address <=> b.address;
Then add the unique constraint
ALTER TABLE votes ADD UNIQUE unique_index(user, email, address);
Verify the constraint with
SHOW CREATE TABLE votes;
Note that user, email, address will be considered unique if any of them has null value in it.
For PostgreSQL...
It didn't work for me with index; it gave me an error, so I did this:
alter table table_name
add unique(column_name_1,column_name_2);
PostgreSQL gave unique index its own name. I guess you can change the name of index in the options for the table, if it is needed to be changed...
I am doing a web app for a client that is an internal system for getting employees to fill out a multitude of forms before they start.
When I have received the forms, there are lots of them like 25-30, and i am having trouble figuring out how to store the data for each of them in a MySQL database. Each of the forms have different data required and different selections.
Initially i was thinking of having a single table, then serializing the selections into a column rather than having a hundred columns. I haven't done anything of this magnitude before and wondered what the best way to save the data would be?
You can use an EAV store, something like:
| form_name | employee_id | field_name | value |
It's not super clean but it's kinda normalized and you'll be able to query on it in a sane fashion. If you run into issues with the table being too large, even with indexes, you can split up the EAV store into multiple tables, possibly by form_name.
You probably want at least three tables:
CREATE TABLE form_fields (
id INT NOT NULL AUTO_INCREMENT,
form_name VARCHAR(50),
field_name VARCHAR(20),
PRIMARY KEY (`id`),
UNIQUE KEY `real_pk` (`formname`,`fieldname`)
);
CREATE TABLE forms_completed (
id INT NOT NULL AUTO_INCREMENT,
form_name VARCHAR(20),
... other info about this instance of the form,
e.g. submitted date, submitted by...
PRIMARY KEY (`id`),
FOREIGN KEY (form_name) REFERENCES form_fields(form_name)
ON DELETE CASCADE
);
CREATE TABLE form_values (
id INT NOT NULL AUTO_INCREMENT,
form_instance IN NOT NULL,
form_name VARCHAR(50), /* only required to force referential integrity */
field_name VARCHAR(20),
date_value DATETIME,
int_value INT,
float_value FLOAT,
string_value VARCHAR(100),
PRIMARY KEY (`id`),
FOREIGN KEY (form_instance) REFERENCES forms_completed(id)
ON DELETE CASCADE,
FOREIGN KEY (form_name, field_name)
REFERENCES form_fields(form_name, field_name)
ON DELETE CASCADE
);
If you're not locked into MySQL, you can try doing this with MongoDB, as it is better suited for tasks like this one. If you are locked in, EAV is probably the way to go.
If you are using this Data for maintaining records its a better idea to use MS Excel.
Or even you can try with the MS Access as alternative to it.