I am designing a new system for users/clients and I have in my system user preferences. Before I start to create code and the database I want to make sure I do the right thing.
I have these preferences:
font size
font face
font color
theme
home page
dashboard options
few true/false options like enable sharing etc...
and more.
My idea was to create each field for each preferences but I thouhgt maybe I can save an object or array instead in a blob.
Is it a good idea?
You could make an auxiliary table to keep references of properties names. Then you can link that table with a merge table — between a user id and the id of one of the properties. Doing so, you can always change the properties.
Using Foreign keys you can also “cascade delete” user details that have a property that does not exist anymore. Furthermore, you can ensure that you add only references to valid property names, and you optimize the search by using indexes.
Let's say that:
CREATE TABLE user_preferences_headers
(
`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL
);
CREATE TABLE `users`
(
`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`pass` VARCHAR(255) NOT NULL
);
CREATE TABLE `user_preferences`
(
`id_user` INT NOT NULL,
`id_preference_entity` INT NOT NULL,
`value` VARCHAR(255)
);
ALTER TABLE `user_preferences` ADD INDEX ( `id_user` );
ALTER TABLE `user_preferences` ADD INDEX ( `id_preference_entity` );
ALTER TABLE `user_preferences` ADD FOREIGN KEY ( `id_user` ) REFERENCES `users` (
`id`
) ON DELETE CASCADE ON UPDATE CASCADE ;
ALTER TABLE `user_preferences` ADD FOREIGN KEY ( `id_preference_entity` ) REFERENCES `user_preferences_headers` (
`id`
) ON DELETE CASCADE ON UPDATE CASCADE ;
Now you first select all headers from user_preferences_headers by name, or not, and you use the id to select the desired preference value of a user (identified also by id) from user_preferences. Notice that when you delete an entry in user_preferences_headers, all entries that link to the id of the deleted row will also be deleted.
Saving them individually will make searching etc possible on each field. If you serialize them, then searching will become difficult/impossible.
You may not need that now, but it may be required further down the line.
Related
I have a database containing over 1,000 item information and I am now developing a system that will have this check the API source via a regular Cron Job adding new entries as they come. Usually, but not always the case, when a new item is released, it will have limited information, eg; Image and name only, more information like description can sometimes be initially withheld.
With this system, I am creating a bulletin to let everyone know new items have been released, so like most announcements, they get submitted to a database, however instead of submitting static content to the database for the bulletin, is it possible to submit something which will be executed upon the person loading that page and that bulletin data is firstly obtained then the secondary code within run?
, For example, within the database could read something like the following
<p>Today new items were released!</p>
<?php $item_ids = "545, 546, 547, 548"; ?>
And then on the page, it will fetch the latest known information from the other database table for items "545, 546, 547, 548"
Therefore, there would be no need to go back and edit any past entries, this page would stay somewhat up-to-date dynamically.
Typically you would do something like have a date field on your items, so you can show which items were released on a given date. Or if you need to have the items associated with some sort of announcement record, create a lookup table that joins your items and announcements. Do not insert executable code in the DB and then pull it out and execute it.
CREATE TABLE `announcements` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`publish_date` DATETIME NOT NULL,
`content` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `items` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` VARCHAR(128) NOT NULL,
`description` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `announcement_item_lkp` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`announcement_id` int(11) unsigned NOT NULL,
`item_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `announcement_item_lkp_uk1` (`announcement_id`,`item_id`),
KEY `announcement_item_lkp_fk_1` (`announcement_id`),
KEY `announcement_item_lkp_fk_2` (`item_id`),
CONSTRAINT `announcement_item_lkp_fk_1` FOREIGN KEY (`announcement_id`) REFERENCES `announcements` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `announcement_item_lkp_fk_2` FOREIGN KEY (`item_id`) REFERENCES `items` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
With the announcement_item_lkp table, you can associate as many items to your announcement as you like. And since you have cascading deletes, if an item gets deletes, its lookup records are deleted as well, so you don't have to worry about orphaned references in your announcements, like you would it you just stuff a string of IDs somewhere.
You're already using a relational database, let it do its job.
ColumnName is Unique (UNIQUE KEY ColumnName).
I just want to make column not unique (must be very simple, but can not understand how).
If in phpMyAdmin check at column name and at bottom click on Unique icon, get #1062 - Duplicate entry '' for key 'RegistrationNumber'. OK, see it is because, clicking on icon it ADD UNIQUE.
There is Unique icon in Structure within row. But the icon is not click-able.
As in phpMyAdmin did not found how to do it, trying with query.
Based on advices tried ALTER TABLE TableName DROP INDEX ColumnName.
Get 1091 Can't DROP 'ColumnName'; check that column/key exists
Here https://stackoverflow.com/a/4414694/2465936 found This error means that you are trying to delete a key which is being used by another table. Possibly the ColumnName is used by another table.
Please advice what need to do to make column not unique.
With SHOW CREATE TABLE get
Array
(
[0] => Array
(
[Table] => 18_6_TransactionPartners
[Create Table] => CREATE TABLE `18_6_TransactionPartners` (
`Number` int(11) NOT NULL AUTO_INCREMENT,
`CompanyName` char(255) COLLATE utf8_unicode_ci NOT NULL,
`RegistrationNumber` char(255) COLLATE utf8_unicode_ci NOT NULL,
.......
PRIMARY KEY (`Number`),
UNIQUE KEY `Number_2` (`Number`),
UNIQUE KEY `CompanyName` (`CompanyName`,`RegistrationNumber`),
KEY `Number` (`Number`)
) ENGINE=InnoDB AUTO_INCREMENT=444 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
)
)
Update
Based on #Bart Friederichs advice tried ALTER TABLE 18_6_TransactionPartners DROP INDEX Number and changed column RegistrationNumber not not unique. Do not understand why (possibly had some mess with unique keys). In any case can change to not unique.
Probably you have a named INDEX. By using SHOW CREATE TABLE tbl you can find out the names of the indices. Then drop them by name (e.g. some test table):
mysql> SHOW CREATE TABLE test;
CREATE TABLE `test` (
`entry_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
UNIQUE KEY `k` (`entry_id`)
)
To drop the index, use this:
ALTER TABLE test DROP INDEX k;
Your key name is RegistrationNumber (as is told by the error message):
ALTER TABLE TableName DROP INDEX RegistrationNumber;
If your column was defined unique using UNIQUE clause, then you can do something like this:
ALTER TABLE mytable DROP INDEX constraint_name
For dropping the index do this:-
ALTER TABLE mytable DROP INDEX index_name;
You have to drop the index using the index name, not the column name.
I've got a user table, and a picture table.
The picture table has an uploader id column which is a foreign key.
It refers to the user id column in the user table.
For some reason, when I try to insert a new record into the picture table, it's not working. No error messages pop up. It just doesn't insert the new record.
INSERT INTO picture (pic_url, pic_uploader) VALUES($picurl, $uploader);
$picurl is an image src path from a file upload. The uploaded files are in the right directory, and the exact same code works perfectly for an earlier record without a foreign key.
$uploader contains the foreign key value -- from a session variable that contains the user id of the user account -- but it's not inserting it into the table.
Here's the SQL for the tables, in case that helps...
Picture table
CREATE TABLE IF NOT EXISTS picture (
pic_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
pic_url varchar(200) NOT NULL,
pic_uploader bigint(20) unsigned NOT NULL,
PRIMARY KEY (pic_id),
KEY pic_uploader (pic_uploader)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Picture constraints
ALTER TABLE picture
ADD CONSTRAINT pictures_ibfk_1 FOREIGN KEY (pic_uploader) REFERENCES user (user_id);
User table
CREATE TABLE IF NOT EXISTS user (
user_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_name varchar(80) NOT NULL,
user_img varchar(200) NOT NULL DEFAULT 'img/defaultpic.png',
user_email varchar(100) NOT NULL,
user_pword char(60) NOT NULL,
user_stat enum('0','1','A') NOT NULL,
PRIMARY KEY (user_id),
UNIQUE KEY user_email (user_email)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
There can be a reason that $uploader value does not exists in your user table as user_id.
First try to insert in simple way directly in db by any tool like sqlyog putty, mysqladmin etc.
INSERT INTO picture (pic_url, pic_uploader) VALUES('pic1', 'user1');
Make sure user1 should exist in user table. If it works fine then problem in your application other wise mysql will return an error by this we will be able to check the exact problem either it is due to foreign key or else. So do it and share results.
IN your picture table you are using columns NOT NULL.
But you try to insert only two values in the insert command that's why data is not inserted.
Either insert data for all columns or make columns as NULLABLE.
Thanks
create table Board (
boardID char(30) not null,
readLevel tinyint not null,
writeLevel tinyint not null,
PRIMARY KEY (boardID) ) engine=InnoDB character set=utf8;
create table Post (
postID int not null AUTO_INCREMENT,
title char(50) not null,
content TEXT not null,
writeDate date not null,
readCount int not null,
PRIMARY KEY (postID)) engine=InnoDB character set=utf8;
create table Save_Board_Post(
boardID char(30) not null,
postID int not null,
FOREIGN KEY (boardID) REFERENCES Board(boardID) ON UPDATE CASCADE,
FOREIGN KEY (postID) REFERENCES Post(postID) ON UPDATE CASCADE ) engine=InnoDB character set=utf8;
insert into Board (boardID, readLevel, writeLevel) values ('testBoard', 0, 0);
insert into Post (title, content, writeDate, readCount) values ('testPost1', 'test', CURRENT_TIMESTAMP() ,0);
select * from Board where boardID='testBoard';
select * from Post where tile='testPost1';
select * from Save_Board_Post where boardID='testBoard';
I'm rookie in sql. and I'm not native about English.
So, Please forgive my English skills.
Here's my mysql code.
Last five lines are for test. And select from Board and Post is working fine.
But
select * from Save_Board_Post where boardID= 'testBoard';
It doesn't work. This code has no error. but there is no output result.
I guess it means no data in Save_Board_Post table.
I thought REFERENCES command is automatically creation data when insert parent table.
If it does not, please let me know how to automatically creation in relation data.
No, that's not what REFERENCES does. All that your REFERENCES constraints mean is that every row that is inserted (manually) into the Save_Board_Post table must have a boardID and a postID that exist in the Board and Post tables. Nothing is inserted into that table automatically.
If you are trying to represent what board a post is in, the appropriate way to do this would be to make the board ID be a property of the post, e.g.
CREATE TABLE Post (
postID INTEGER NOT NULL AUTO_INCREMENT,
boardID CHAR(30) NOT NULL,
...
FOREIGN KEY (boardID) REFERENCES Board(boardID)
);
rather than having an entirely separate table just for that data.
You cannot automatically insert data in child table by inserting in the parent table. You got it right when you said it failed because there was no data in the table. Referential integrity exist to remove redundancy in a database. I dont think there is a way for you to automatically insert into the child table by inserting into a parent table. you have to do it manually.
I would like to have a way of controlling/tracking revisions of rows. I am trying to find the best solution for this problem.
The first thing that comes to mind is to have a table with a id to identify the row and and id for the revision number. The combined ids would be the primary key. so example data might look like this:
1, 0, "original post"
1, 1, "modified post"
1, 2, "modified again post"
How can I create a table with this behavior? or is there a better solution to do this?
I like InnoDB since it supports transactions, foreign keys and full text in MySQL 5.6+.
I know its possible to "force" this behavior by how I insert the data but I'm wondering if there is a way to have the table do this automatically.
Consider table structure:
TABLE posts
post_id INT AUTO_INCREMENT PK
cur_rev_id INT FK(revisions.rev_id)
TABLE revisions
rev_id INT AUTO_INCREMENT PK
orig_post INT FK(posts.post_id)
post_text VARCHAR
Where the posts table tracks non-versioned information about the post and its current revision, and revisions tracks each version of the post text with a link back to the parent post. Because of the circular FK constraints you'd need to enclose new post insertions in a transaction.
With this you should be able to easily add, remove, track, roll back, and preview revisions to your posts.
Edit:
Yeah, enclosing in a transaction won't exactly help since the keys are set to AUTO_INCREMENT, so you need to dip back in to PHP with LAST_INSERT_ID() and some temporarily NULL indexes.
CREATE TABLE `posts` (
`post_id` INT(10) NOT NULL AUTO_INCREMENT,
`cur_rev_id` INT(10) NULL DEFAULT NULL,
`post_title` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`post_id`),
INDEX `FK_posts_revisions` (`cur_rev_id`),
) ENGINE=InnoDB
CREATE TABLE `revisions` (
`rev_id` INT(10) NOT NULL AUTO_INCREMENT,
`orig_post` INT(10) NULL DEFAULT NULL,
`post_text` VARCHAR(32000) NULL DEFAULT NULL,
PRIMARY KEY (`rev_id`),
INDEX `FK_revisions_posts` (`orig_post`),
) ENGINE=InnoDB
ALTER TABLE `posts`
ADD CONSTRAINT `FK_posts_revisions` FOREIGN KEY (`cur_rev_id`) REFERENCES `revisions` (`rev_id`);
ALTER TABLE `revisions`
ADD CONSTRAINT `FK_revisions_posts` FOREIGN KEY (`orig_post`) REFERENCES `posts` (`post_id`);
Then:
$db_engine->query("INSERT INTO posts (cur_rev_id, post_title) VALUES (NULL, 'My post Title!')");
$post_id = $db_engine->last_insert_id();
$db_engine->query("INSERT INTO revisions (orig_post, post_text) VALUES($post_id, 'yadda yadda')");
$rev_id = $db_engine->last_insert_id();
$db_engine->query("UPDATE posts SET cur_rev_id = $rev_id WHERE post_id = $post_id");
If I've understood you correctly and the table doesn't receive large numbers of updates/deletes then you could look at setting a trigger such as:
DELIMITER $$
CREATE TRIGGER t_table_update BEFORE UPDATE ON table_name
FOR EACH ROW
INSERT INTO table_name_revisions (item_id, data, timestamp)
VALUES(OLD.id, OLD.data, NOW());
END$$
DELIMITER ;
See trigger syntax for more information