perfect database design - mysql

This is my table schema,
below am is the schema for feedback table functionality,
please give good suggestion for the length and naming sense for the table and its fields,
please share your suggestion about the table schema and its length and naming conventions.
CREATE TABLE `mytest`.`tbl_feedback` (
`feedback_id` INT( 30 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`first_name` VARCHAR( 50 ) NOT NULL ,
`last_name` VARCHAR( 50 ) NOT NULL ,
`email_id` VARCHAR( 150 ) NOT NULL ,
`comment` TEXT NOT NULL ,
`cur_timestamp` VARCHAR( 20 ) NOT NULL ,
`ipaddress` VARCHAR( 20 ) NOT NULL ,
`status` INT( 3 ) NOT NULL DEFAULT '0'
) ENGINE = MYISAM ;
also i saw in this thread varbinary,
here they said use varbinary isntead of varchar, why should we go for varbinary, what is the advantage

Try to place fixed length fields (such as 'status') before the variable length fields. This won't make any difference whatsoever to the logic, but the program should run slightly faster when accessing those fields.

Here are just a few observations:
Table name "tbl_feedback" - This is a personal preference, but I would remove the "tbl_" prefix - I've never run into a case where I've found it useful to have prefixes on database objects.
email_id - I would take off the "_id" suffix. ID is a common naming convention for key columns (primary or foreign), and you seem to be using that convention too. In your case, it doesn't seem like email_id is a foreign key though, so just "email" might be better.
cur_timestamp - A name like "created_date" or "updated_date" might make a little more sense. With cur_timestamp, it's not really clear what the column is for, or if you're supposed to update it when you update the record... etc. Also, for this column, it might be useful to use a built-in type that can represent a date, rather than a varchar.
ipaddress - Just a nitpick, but based on your naming convention, it might be better if this was "ip_address". Also, you probably want to make this column at least 40 characters or bigger, so you can store IPv6 addresses (in the common notation).
status - does this column point to another "status" table? If so, should this be a foreign key named "status_id"?

Some of the fields look they the could be normalized.. like status as foreign key constraint, to a status table, and people as a separate table with a personid as a FK. Then you could do cool things like look up all feedback by person, or delete all by person, etc.

Related

What's the best practice to design a table that would have different fields on different conditions?

I need advice in creating tables where there would be different fields based on a condition. I'm pretty new to psql, so I don't really know if I'm going the right way and would appreciate any tips / advice!
Currently I have a table to represent a meeting_note, which can either be a voice recording OR a text.
When the meeting note is of type text, it must have a meeting_content, and can have an optional meeting_summary. audio_source should be null.
When the meeting note is of type audio, it must have an audio_source and the fields meeting_content and meeting_summary should be null.
I was also thinking of creating two tables - one for type audio and another for text, but there is a unique constraint on created_at which represents a date like May 11th. I wasn't sure how to add this constraint between two tables.
Here are the fields for the table meeting_note
id serial PRIMARY KEY,
meeting_id integer REFERENCES meeting(id),
meeting_note_type enum('audio', 'text') NOT NULL,
meeting_content text,
summary varchar(255),
created_at varchar(10) NOT NULL,
recording_source varchar(255)
and the constraints:
UNIQUE (to_char(created_at, 'YYYY-MM-DD')),
CHECK (NOT (meeting_note_type = 'text' AND meeting_content IS NULL)),
CHECK (NOT (meeting_note_type = 'audio' AND audio_source IS NULL)),
CHECK (NOT (meeting_content IS NULL AND audio_source IS NULL),
CHECK (NOT (meeting_content IS NOT NULL AND audio_source IS NOT NULL),
CHECK (NOT (audio_source IS NOT NULL AND summary IS NOT NULL))
Appreciate any help on this. Thank you so much in advance!
There are two common approaches to this problem - using a table-per-type and using one table for everything. The approach you describe in the question is one table for everything; your definition is pretty accurate.
Here is how to do a table-per-type solution: make a "master" table for all notes, and then a table for each note sub-type, like this:
create table note_master(
id serial PRIMARY KEY,
meeting_id integer REFERENCES meeting(id),
created_at varchar(10) NOT NULL
)
create table note_text (
id serial REFERENCES note_master(id),
meeting_content text,
summary varchar(255),
)
create table note_audio (
id serial REFERENCES note_master(id),
recording_source varchar(255)
)
To query for everything you do left-outer joins to note_text and note_audio. This approach lets you skip the enum because you can always figure out what kind of note it is by examining the results of the join.

MYSQL Long super-keys

I am currently working on a project, which involves altering data stored in a MYSQL database. Since the table that I am working on does not have a key, I add a key with the following command:
ALTER TABLE deCoupledData ADD COLUMN MY_KEY INT NOT NULL AUTO_INCREMENT KEY
Due to the fact that I want to group my records according to selected fields, I try to create an index for the table deCoupledData that consists of MY_KEY, along with the selected fields. For example, If I want to work with the fields STATED_F and NOT_STATED_F, I type:
ALTER TABLE deCoupledData ADD INDEX (MY_KEY, STATED_F, NOT_STATED_F)
The real issue is that the fields that I usually work with are more than 16, so MYSQL does not allow super-keys longer than 16 fields.
In conclusion, Is there another way to do this? Can I make (somehow) MYSQL to order the records according to the desired super-key (something like clustering)? I really need to make my script faster and the main overhead is that each group may contain records which are not stored on the same page of the disk, and I assume that my pc starts random I/Os in order to retrieve records.
Thank you for your time.
Nick Katsipoulakis
CREATE TABLE deCoupledData (
AA double NOT NULL DEFAULT '0',
STATED_F double DEFAULT NULL,
NOT_STATED_F double DEFAULT NULL,
MIN_VALUES varchar(128) NOT NULL DEFAULT '-1,-1',
MY_KEY int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (MY_KEY),
KEY AA (AA) )
ENGINE=InnoDB AUTO_INCREMENT=74358 DEFAULT CHARSET=latin1
Okay, first of all, when you add an index over multiple columns and you don't really use the first column, the index is useless.
Example: You have a query like
SELECT *
FROM deCoupledData
WHERE
stated_f = 5
AND not_stated_f = 10
and an index over (MY_KEY, STATED_F, NOT_STATED_F).
The index can only be used, if you have another AND my_key = 1 or something in the WHERE clause.
Imagine you want to look up every person in a telephone book with first name 'John'. Then the knowledge that the book is sorted by last name is useless, you still have to look up every single name.
Also, the primary key does not have to be a surrogate / artificial one. It's nearly always better to have a primary key which is made up of columns which identify each row uniquely anyway.
Also it's not always good to have many indexes. Not only do indexes slow down INSERTs and UPDATEs, sometimes they just cause an extra lookup, since first a look at the index is taken and a second look to find the actual data.
That's just a few tips. Maybe Jordan's hint is not a bad idea, "You should maybe post a new question that has your actual SQL query, table layout, and performance questions".
UPDATE:
Yes, that is possible. According to manual
If you define a PRIMARY KEY on your table, InnoDB uses it as the clustered index.
which means that the data is practically sorted on disk, yes.
Be aware that it's also possible to define a primary key over multiple columns!
Like
CREATE TABLE deCoupledData (
AA double NOT NULL DEFAULT '0',
STATED_F double DEFAULT NULL,
NOT_STATED_F double DEFAULT NULL,
MIN_VALUES varchar(128) NOT NULL DEFAULT '-1,-1',
MY_KEY int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (NOT_STATED_F, STATED_F, AA),
KEY AA (AA) )
ENGINE=InnoDB AUTO_INCREMENT=74358 DEFAULT CHARSET=latin1
as long as the combination of the columns is unique.

Dynamic ENUMs in MySQL/JDBC

I am about to design a database for a logging system.
Many of the String columns will have a limited amount of values, but not known in advance:
Like the name of the modules sending the messages, or the source hostnames.
I would like to store them as MySQL Enums to save space.
So the idea would be that the Enums grow as they encounter new values.
I would start with a column like :
host ENUM('localhost')
Then, in Java, I would load on startup the enum values defined for the hostnames at a time (how do I do that with MySQL/JDBC ??), and I would alter the Enum whenever I encounter a new host.
Do you think it is feasible / a good idea ?
Have you ever done something like that ?
Thanks in advance for your advice.
Raphael
This is a not good idea. ENUM designed not for that.
You can just create separate table (host_id, host_name) and use refference in main table. Example:
CREATE TABLE `host` (
`host_id` INT(10) NOT NULL AUTO_INCREMENT,
`host_name` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`host_id`)
)
CREATE TABLE `log` (
`log_id` INT(10) NOT NULL AUTO_INCREMENT,
`host_id` INT(10) NULL DEFAULT NULL,
...
PRIMARY KEY (`log_id`),
INDEX `FK__host` (`host_id`),
CONSTRAINT `FK__host` FOREIGN KEY (`host_id`) REFERENCES `host` (`host_id`) ON UPDATE CASCADE ON DELETE CASCADE
)
UPD:
I think the best way to storing host is varchar/text field. It is easiest and fastest way. I think you need not worry about the space.
Nonetheless.
Using the second table for hosts will reduce the size, but will complicate writing logs. Using ENUM complicate writing and significantly reduce the performance.

Need some tips on create table

I plan to create a table to store the race result like this:
Place RaceNumber Gender   Name  Result
12 0112 Male Mike Lee 1:32:40
16 0117 Female Rose Mary 2:20:40
I am confused at the items type definitions.
I am not sure the result can be set to varchar(32) or other type?
and for racenumber, between int(11) and varchar(11), which one is better?
Can I use UNIQUE KEY like my way?
Do I need to split name to firstname and lastName in my DB table?
DROP TABLE IF EXISTS `race_result`;
CREATE TABLE IF NOT EXISTS `race_result` (
`id` int(11) NOT NULL auto_increment,
`place` int(11) NOT NULL,
`racenumber` int(11) NOT NULL,
`gender` enum('male','female') NOT NULL,
`name` varchar(16) NOT NULL,
`result` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `racenumber` (`racenumber`,`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 AUTO_INCREMENT=3;
Some advice/opinions regarding datatypes.
Result - This is a time, you may want to do some calculations on this time, therefore you should store it as a time type.
RaceNumber - This is a reference, whilst it is a number, you will be performing no calculations on this number. Therefore you should store it as a varchar rather than an int. This will avoid confusion as to its usage and avoid accidently manipulation of it as a number.
Name - Look at the length of string you allow for the Name. Be careful about limiting this value by so much. 16 characters may be too small for some names in the future.
Place - Is this required storage? Can you calculate the place of a runner based on their Result alone? However, you should keep a good primary key for your table.
In answer to your specific questions:
Result: I would just set the result to an integer number of seconds. My opinion is that data should be stored in databases, not formatting. Since the likely things you're going to want to do with this is sort by it and return rows less than or greater than specific values of it, an integer seems better to me.
Race number: Same for race number. If it's always going to be numeric, use an integer and worry about the formatting in the application. If it can be non-numeric then by all means make it varchar but, for a numeric value, I can't see enough gain in making it so.
Unique key: I don't really see the point in having a unique index on race number and ID. ID is, by definition, already unique as a primary key. Perhaps you meant race number and place although even that is risky in the event of two people drawing for a place.
Split names: If you're ever going to treat them as individual values, then yes. Otherwise no. In other words, avoid things like where fullname like 'Mike %'.
For the name, if you ever want to sort on lastname, while you display it as "firstname lastname", then you will need to use separate columns.
In general: think about what you want to do with the data. Leave formatting to the application that is displaying the data. Avoid situations where you need string manipulation or complicated maths to get at the values you need.

Unable to create column in MySQL database with datatype of TEXT

I'm creating a table to hold items from rss feeds and I need to create a column 'description'. I selected the datatype as TEXT with no limit set with no index on this column. This is the error I'm getting:
#1071 - Specified key was too long; max key length is 1000 bytes
If I select to index this column, I get this error message:
#1170 - BLOB/TEXT column 'description' used in key specification without a key length
Any length I specify for this column returns the first error I got. Can someone tell me what I'm doing wrong? Thanks!
UPDATE:
I'm using MySQL 5.0.4
Here is the query I'm using to create the table:
CREATE TABLE `feed_items` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`feed_id` INT NOT NULL COMMENT '`feeds`.`id`',
`guid` VARCHAR( 255 ) NOT NULL ,
`publish_date` DATETIME NOT NULL ,
`update_of` INT NULL COMMENT '`feed_items`.`id`',
`link` VARCHAR( 255 ) NOT NULL ,
`title` VARCHAR( 255 ) NOT NULL ,
`description` TEXT NOT NULL ,
`comments_link` VARCHAR( 255 ) NULL ,
INDEX ( `feed_id` , `guid` , `publish_date` , `update_of` , `title` )
) ENGINE = MYISAM
Even though it was from a while, this bug report seems to be what you are having an issue with:
http://bugs.mysql.com/bug.php?id=4541
You may want to read through the comments on this bug, and see if they are referring to your situation, since it is hard to help without seeing what command you used to get this error, nor knowing which version of mysql.
UPDATE: Based on your query, you may want to remove this first:
INDEX ( `feed_id` , `guid` , `publish_date` , `update_of` , `title` )
Then, try doing:
INDEX ( `feed_id`)
When I see a long index like this I get suspicious.
Ideally you should index what you will be doing selects on, so unless you have
SELECT * FROM table WHERE feed_id=? AND guid=? AND publish_date=? AND update_of=? and title=?
then this index is useless. Since feed_id is a primary key you should just do
SELECT * FROM table where feed_id=?
as it will uniquely return one row.
So the index I have above would be what is needed. You may also want a separate index on title and perhaps publish_date.
Look at what queries you are writing, then you can determine where indexes should be. Indexes will get expensive as you add more rows, so you don't want to index too much, but indexing too little is useless, so that is why I suggest you look at the queries then decide where to have indexes.