Referring MySQL ENUM in another table - mysql

I have a table SkillLevel created as
CREATE TABLE `sklllevel` (
`Name` varchar(20) NOT NULL,
`level` enum('No Experience','Beginner','Expert','Advisor') DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
With Values
INSERT INTO test.sklllevel (name,level) values ('No Experience','No Experience'),('Beginner','Beginner'),('Expert','Expert'),('Advisor','Advisor');
I want to refer SkillLevel.Level with testSkill.tkSkill in another table created as:
CREATE TABLE `testskill` (
`pkid` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
`tkSkill` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`pkid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DO I need to have tkSkill as ENUM with same set of Values to set a foreign key? What is the best practise here?

Short answer: enums are stored as a number, so technically, you could join them with tkSkill as a tinyint.
To use it as a foreign key, you indeed need to have both tkSkill and level to be the same enum - but you need level to be a unique column to qualify as a foreign key, so add unique to it (to be really precise: for InnoDB, you can have non-unique foreign keys if you manually create the index, but non-unique foreign keys are generally a bad idea). But you should think about what your key in sklllevel should be, since now it looks as if you want Name to be the key.
And independently from having it as key you should define tkSkill as (the same) enum to make sure they both mean the same if you at one point would like to change the enums (what is a bad idea!) and e.g. add another skilllevel; or if you want to "read" (directly understand) the value when you select directly from the table testskill without the need to join sklllevel; and if you want to insert values into tkSkill by using their enum-namoe (e.g. 'Expert' instead of 3, but you can use both) without looking them up in sklllevel.
Longer answer: Best practice is: don't use enums. Depending on "belief", there is none to only a handful of cases when enums might be slightly useful, if at all. One could be that you don't want to use a reference table to skip a join to display the textvalue/description of an integer-id. In your setup, you are actually using a reference table and still want to use enums.
The closest you'll get to best practice using enums would be to define tkSkill as enum in testskill and don't have the sklllevel-table (the reference table) at all.
But again, I would urge you not to use enums. You can define your table as
CREATE TABLE `sklllevel` (
`Id` tinyint(4) primary key,
`Name` varchar(20) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
and then use that id as a foreign key for tkSkill. Or even
CREATE TABLE `sklllevel` (
`Name` varchar(20) primary key
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
and then define tkSkill as varchar(20) and use this as a foreign key - it will use more space, though, but you will have "readable" values in the table if that was the reason for you to use enums in the first place.
Here you can find some background to enums: 8 Reasons Why MySQL's ENUM Data Type Is Evil

Related

Remember me table indexes

I am creating a table in MariaDB to store "remember me"-cookie values:
CREATE TABLE u_remember_me (
u_user_common_id INT UNSIGNED NOT NULL,
lookup_key VARCHAR(30) NOT NULL,
token_hash VARCHAR(30) NOT NULL,
created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX (u_user_common_id),
FOREIGN KEY (u_user_common_id) REFERENCES u_user_common (id) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (lookup_key),
UNIQUE KEY (lookup_key, token_hash)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci ENGINE InnoDB;
I am always going to lookup by the lookup_key field and have therefore chosen that as a primary key (random string). The column u_user_common_id can not be set as primary key since a user can have multiple (lookup_key, token_hash) assigned because of multiple browsers or computers. Is lookup_key a bad choice for a primary key (performance, insert etc.?). Also.. Should I set the primary key to (lookup_key, token_hash) instead and drop the UNIQUE constraint? The reason why I have chosen not to have a surrogate key is because it will never be used, but I am not sure if that is a good decision either (I need lookup_key to be indexed anyway).
I think you have not figured out what depends on what.
Is the lookup_key (random string) associated with a single user? If so, PRIMARY KEY(lookup_key) and get rid of token_hash.
If the user can connect from different browsers and you want something different to happen, then you have not specified any data that will be different.

MySQL column used to sort - auto increment

I have a table with primary id auto generated and a separate order column (integer which might be changed by a GUI application). I would like the order column to be auto incremented when new row is inserted. It can be the same value as new car_id, I don't care. Is it possible?
I think I can have more then one auto increment fields, but they both need to be part of the primary key, and to prevent to potentially have two cars with same car_id I would need unique index on car_id? Am I correct?
I think I can use triggers, but they are prohibited by my hosting company.
CREATE TABLE IF NOT EXISTS `car` (
`car_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`description` text,
`order` int(11) DEFAULT NULL,
PRIMARY KEY (`car_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
Why I would like it to be handled by the database is because I use a complicated system of database handling in my program (using generic types and reflection to make everything automated) and this feature would make my life easier.

mysql - referencing one foreign key to multiple possible primary keys

I'd like to set up the following database scenario:
CREATE TABLE IF NOT EXISTS `points` (
`po_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`po_north` INT,
`po_east` INT,
PRIMARY KEY (`po_id`),
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `lines`(
`li_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`li_from` INT NOT NULL,
`li_to` INT NOT NULL,
PRIMARY KEY (`li_id`),
FOREIGN KEY (`li_from`) REFERENCES points(`po_id`),
FOREIGN KEY (`li_to`) REFERENCES points(`po_id`),
) ENGINE=InnoDB;
Now I want to set up a third table, that sores some metadata like who created or altered a point or a line:
CREATE TABLE IF NOT EXISTS `metadata` (
`me_type` ENUM('point','line') NOT NULL,
`me_type_id` INT UNSIGNED NOT NULL,
`me_permissions` VARCHAR(255) NOT NULL,
`me_created_by` INT UNSIGNED NOT NULL,
`me_created_on` DATETIME NOT NULL,
`me_last_modified_by` INT UNSIGNED NOT NULL,
`me_last_modified_by` DATETIME NOT NULL,
) ENGINE=InnoDB;
My first approach was to set an ENUM with two types (points and lines). But the problem is still, that I cannot properly reference a foreign key to one of the tables. Is there any recommended solution for such problem in MySQL?
BTW:
The fields for me_created_by and me_last_modified_by shall reference to a table storing some user data.
Your case appears to be yet another instance of the design pattern known as "generalization specialization" or perhaps "table design for class inheritance".
If you think of points and lines as classes of objects, they are both subclasses of some more general class of objects. I'm not sure what name to give the superclass in this case. Here's one of several previous questions that address the same issue.
Extending classes in the database
Fowler gives an extensive treatment of the subject. Your case has an added wrinkle, because you are dealing with metadata. But that need not alter the design. You need a third table, which I'll call "Items" for lack of a better term. The key, "it_id" would be assigned an auto number, and you would add an item every time you add either a point or a line. The two columns "po_id" and "li_id" would not be assigned an auto number. Instead they would be foreign keys, referencing "it_id" in the Items table.
The references to points or lines in the metadata table would then be references to "items" and you could use that information to find information about points or lines as the case may be.
How helpful this is depends on what you are trying to do with the metadata.
Your tables points and lines should contain a foreign key to metadata – not the other way around. Doing so will save you from defining any more complicated table setups. Using this approach, a single metadata-entry could be re-used several times for many different points or lines. This isn't even MySQL specific but a general, normalized database structure.
you can do this using a trigger, you need to trigger an event that can create reference key for either point or line before you insert a record based on respective tables

PRIMARY KEY definition in MySQL CREATE TABLE statement

What's the difference between this code:
CREATE TABLE samples (
sampleid INT(11) NOT NULL AUTO_INCREMENT,
sampledate DATE NOT NULL,
location VARCHAR(25) NOT NULL,
PRIMARY KEY (sampleid)
)
ENGINE=InnoDB;
and this:
CREATE TABLE samples (
sampleid INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
sampledate DATE NOT NULL,
location VARCHAR(25) NOT NULL,
)
ENGINE=InnoDB;
code?
So a separate PRIMARY KEY statement or as part of a column definition. Same question for UNIQUE INDEX and UNIQUE keyword in column definition.
The second syntax is merely a shortcut allowing you to specify the column and add an index on it in a single clause.
This works out fine in cases where you simply want to create a column and add an index on it.
You'll need to use the first syntax if you want to do something more complicated, such as adding an index based on multiple columns rather than a single column, or if you are adding or changing an index on an existing column; that is, you are not creating the column and the index on it at the same time.
MySQL allows uses the PRIMARY KEY directive to allow you to set the Primary Key dynamically. Supplying PRIMARY KEY as an argument to the constructor can only be called on creating the column. PRIMARY KEY(X), PRIMARY KEY(Y), PRIMARY KEY(Z) allows for changing the primary keys on subsequent queries.
The way I see it is.. The first method is used to create composite keys. While the second method (more readable to me) is primarily used if there is only primary key in the table.
The second method cannot be used if you want to implement composite key
There are many ways to skin a cat and above 2 examples are just 2 of them. They are identical. There's no difference.
They are literally the same. Here is a quick site that shows you the different ways (3) to do it. http://www.java2s.com/Code/SQL/Key/Defineanduseprimarykey.htm

Which way to define foreign keys in MySQL

I see two ways it is done:
Method 1:
CREATE TABLE IF NOT EXISTS `sample` (
`sample_id` tinyint(2) NOT NULL AUTO_INCREMENT,
`description` varchar(32) NOT NULL,
`parent_id` int(10) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`sample_id`)
) ENGINE=InnoDB;
ALTER TABLE sample ADD CONSTRAINT parent_id FOREIGN KEY (parent_id) REFERENCES parent_tbl(parent_id);
Method 2:
CREATE TABLE IF NOT EXISTS `sample` (
`sample_id` tinyint(2) NOT NULL AUTO_INCREMENT,
`description` varchar(32) NOT NULL,
`parent_id` int(10) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`sample_id`),
Foreign Key (parent_id) references parent_tbl(parent_id)
) ENGINE=InnoDB;
Which way is better or when to use one over the other?
If you need to add a foreign key to an existing table, use method 1, if you are creating the schema from scratch use method 2.
There isn't a best way, they do the same thing.
The first gives you more flexibility.
1) You are required to use the first method if you create the tables in an order such that a referenced table is created after its referencing table. If you have loops in your references then there may not be a way to avoid this. If there are no loops then there exists an order where all referenced tables are created before their referenced tables, but you may not want to spend time figuring out what that order is and rearranging your scripts.
2) It's not always the case that you know exactly what indexes you will need when you create the table. When you create indexes it is usually a good idea to measure the performance gain on some real data, and perhaps try multiple different indexes to see which works better. For this strategy to work you need to first create the table, insert some data and then you need to be able to modify the indexes for testing. Dropping and recreating the table is not as practical as ALTER TABLE in this situation.
Other than that there isn't really any difference and if you are starting from nothing there is no particular reason to favour one over the other. The resulting index is the same either way.
The end products are indistinguishable.
For clarity (it's nice to see the constraint explictly stand on it's own), I might advocate for the first.
For succinctness (saying the same thing in 1 statement vs 2), I'd might advocate for the second.