I know how to use INDEX as in the following code. And I know how to use foreign key and primary key.
CREATE TABLE tasks (
task_id int unsigned NOT NULL AUTO_INCREMENT,
parent_id int unsigned NOT NULL DEFAULT 0,
task varchar(100) NOT NULL,
date_added timestamp NOT NULL,
date_completed timestamp NULL,
PRIMARY KEY ( task_id ),
INDEX parent ( parent_id )
)
However I found a code using KEY instead of INDEX as following.
CREATE TABLE orders (
order_id int unsigned NOT NULL AUTO_INCREMENT,
-- etc
KEY order_date ( order_date )
)
I could not find any explanation on the official MySQL page. Could anyone tell me what is the differences between KEY and INDEX?
The only difference I see is that when I use KEY ..., I need to repeat the word, e.g. KEY order_date ( order_date ).
There's no difference. They are synonyms, though INDEX should be preferred (as INDEX is ISO SQL compliant, while KEY is a MySQL-specific, non-portable, extension).
From the CREATE TABLE manual entry:
KEY is normally a synonym for INDEX. The key attribute PRIMARY KEY can also be specified as just KEY when given in a column definition. This was implemented for compatibility with other database systems.
By "The key attribute PRIMARY KEY can also be specified as just KEY when given in a column definition.", it means that these three CREATE TABLE statements below are equivalent and generate identical TABLE objects in the database:
CREATE TABLE orders1 (
order_id int PRIMARY KEY
);
CREATE TABLE orders2 (
order_id int KEY
);
CREATE TABLE orders3 (
order_id int NOT NULL,
PRIMARY KEY ( order_id )
);
...while these 2 statements below (for orders4, orders5) are equivalent with each other, but not with the 3 statements above, as here KEY and INDEX are synonyms for INDEX, not a PRIMARY KEY:
CREATE TABLE orders4 (
order_id int NOT NULL,
KEY ( order_id )
);
CREATE TABLE orders5 (
order_id int NOT NULL,
INDEX ( order_id )
);
...as the KEY ( order_id ) and INDEX ( order_id ) members do not define a PRIMARY KEY, they only define a generic INDEX object, which is nothing like a KEY at all (as it does not uniquely identify a row).
As can be seen by running SHOW CREATE TABLE orders1...5:
Table
SHOW CREATE TABLE...
orders1
CREATE TABLE orders1 ( order_id int NOT NULL, PRIMARY KEY ( order_id ))
orders2
CREATE TABLE orders2 ( order_id int NOT NULL, PRIMARY KEY ( order_id ))
orders3
CREATE TABLE orders3 ( order_id int NOT NULL, PRIMARY KEY ( order_id ))
orders4
CREATE TABLE orders4 ( order_id int NOT NULL, KEY ( order_id ))
orders5
CREATE TABLE orders5 ( order_id int NOT NULL, KEY ( order_id ))
Here is a nice description about the "difference":
"MySQL requires every Key also be indexed, that's an implementation
detail specific to MySQL to improve performance."
Keys are special fields that play very specific roles within a table, and the type of key determines its purpose within the table.
An index is a structure that RDBMS(database management system) provides to improve data processing. An index has nothing to do with a logical database structure.
SO...
Keys are logical structures you use to identify records within a table and indexes are physical structures you use to optimize data processing.
Source: Database Design for Mere Mortals
Author: Michael Hernandez
It is mentioned as a synonym for INDEX in the 'create table' docs:
MySQL 5.5 Reference Manual :: 13 SQL Statement Syntax :: 13.1 Data Definition Statements :: 13.1.17 CREATE TABLE Syntax
#Nos already cited the section and linked the help for 5.1.
Like PRIMARY KEY creates a primary key and an index for you,
KEY creates an index only.
A key is a set of columns or expressions on which we build an index.
While an index is a structure that is stored in database, keys are strictly a logical concept.
Index help us in fast accessing a record, whereas keys just identify the records uniquely.
Every table will necessarily have a key, but having an index is not mandatory.
Check on https://docs.oracle.com/cd/E11882_01/server.112/e40540/indexiot.htm#CNCPT721
Related
I'm porting some Postgres SQL to MySQL and am trying to set the starting values of three columns to specific values. The table is as follows:
CREATE TABLE ITEM (
ORDID NUMERIC(4) NOT NULL,
ITEMID NUMERIC(4) NOT NULL,
PRODID NUMERIC(6),
ACTUALPRICE NUMERIC(8,2),
QTY NUMERIC(8),
ITEMTOT NUMERIC(8,2),
CONSTRAINT ITEM_FOREIGN_KEY FOREIGN KEY (ORDID) REFERENCES ORD (ORDID),
CONSTRAINT ITEM_PRIMARY_KEY PRIMARY KEY (ORDID,ITEMID));
The code I'm trying to port is as follows:
CREATE SEQUENCE ORDID
INCREMENT BY 1
START WITH 622
;
CREATE SEQUENCE PRODID
INCREMENT BY 1
START WITH 200381
;
CREATE SEQUENCE CUSTID
INCREMENT BY 1
START WITH 109
;
However, when trying to run this I'm getting the error:
SQL query:
CREATE SEQUENCE ORDIDINCREMENT BY 1 START WITH 622 ;
MySQL said: Documentation
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SEQUENCE ORDID
INCREMENT BY 1
START WITH 622' at line 1
I know that there is no direct equivalent to a SEQUENCE in MySQL but I can't figure out a reasonable way to achieve the same thing without it. Any ideas?
MySQL uses AUTO_INCREMENT for that purpose. Rather than making new sequence types, you apply it to an existing integer column.
Unfortunately you can only have one per table.
There can be only one AUTO_INCREMENT column per table, it must be indexed, and it cannot have a DEFAULT value.
And they must be integers, numeric doesn't work. This will probably improve your schema as 9999 orders and items seems very small.
AUTO_INCREMENT applies only to integer and floating-point types.
And if that wasn't enough, you can't have an AUTO_INCREMENT on a multi-key primary key. Only the vastly inferior MyISAM table format allows that.
So you cannot easily translate your PostgreSQL tables to MySQL verbatim.
You sure you want to convert to MySQL?
In your case, item.ordid is a reference so it will be incremented in its own table. item.prodid is probably also a reference and somebody forgot to declare it that. This leaves just item.itemid to be declared AUTO_INCREMENT, but it's part of the primary key. It probably doesn't need to be, it can just be unique.
In fact, the ITEM table seems more like it's tracking orders of products, not items... but then there's also a product ID? I don't know what an "item" is.
You wind up with something like this:
CREATE TABLE ITEM (
ITEMID INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
PRODID INTEGER REFERENCES PROD(PRODID),
ORDID INTEGER NOT NULL REFERENCES ORD (ORDID),
ACTUALPRICE NUMERIC(8,2),
QTY NUMERIC(8),
ITEMTOT NUMERIC(8,2),
UNIQUE(ORDID, ITEMID)
)
CREATE TABLE ORD (
ORDID INTEGER PRIMARY KEY AUTO_INCREMENT,
...
) AUTO_INCREMENT = 622;
CREATE TABLE PROD (
PRODID INTEGER PRIMARY KEY AUTO_INCREMENT,
...
) AUTO_INCREMENT = 200381;
You can also set the AUTO_INCREMENT starting point after the fact with ALTER TABLE. Because it's a table attribute, not a column attribute, it happens on the table itself.
ALTER TABLE CUST AUTO_INCREMENT=109;
It's largely unnecessary to set the AUTO_INCREMENT starting point if you're importing an existing data set. AUTO_INCREMENT will always use MAX(column) and it cannot be set lower than this. It doesn't matter what you start it at if the table is already populated.
You can create a table with an AUTO_INCREMENT field and set its initial value.
create table myseq(
my_id int auto_increment primary key
) auto_increment=100;
Or use ALTER TABLE to reset the value whenever you want:
alter table myseq auto_increment = 100;
You can use table with AUTO_INCREMENT key to emulate sequences:
CREATE TABLE ORDID (id INT PRIMARY KEY AUTO_INCREMENT) AUTO_INCREMENT = 622;
CREATE TABLE PRODID (id INT PRIMARY KEY AUTO_INCREMENT) AUTO_INCREMENT = 200381;
CREATE TABLE CUSTID (id INT PRIMARY KEY AUTO_INCREMENT) AUTO_INCREMENT = 109;
Each of the table represents a 'sequence'. To use one in your CREATE TABLE:
CREATE TABLE ITEM (
ORDID INT NOT NULL,
ITEMID NUMERIC(4) NOT NULL,
PRODID NUMERIC(6),
ACTUALPRICE NUMERIC(8,2),
QTY NUMERIC(8),
ITEMTOT NUMERIC(8,2),
CONSTRAINT ITEM_FOREIGN_KEY FOREIGN KEY (ORDID) REFERENCES ORDID (ID),
CONSTRAINT ITEM_PRIMARY_KEY PRIMARY KEY (ORDID,ITEMID));
You can then use INSERT to get a new value from your 'sequence':
INSERT INTO ordid VALUES (null);
SELECT LAST_INSERT_ID();
I'm using MySQL 5.5, and I have an existing table in production that stores customer transactions. A simplified version of the table is:
CREATE TABLE transactions (
id INT NOT NULL AUTO_INCREMENT,
description CHAR(100),
posted DATE,
PRIMARY KEY (id)
) ENGINE=MyISAM
We are exploring the idea of using partitioning on the transaction date to make reports that use date filtering execute faster. The following attempt fails because of restrictions on primary keys and partitions explained in MySQL Partitioning Keys documentation.
mysql> CREATE TABLE transactions (
-> id INT NOT NULL AUTO_INCREMENT,
-> description CHAR(100),
-> posted DATE,
-> PRIMARY KEY (id)
-> ) ENGINE=MyISAM
-> PARTITION BY HASH(MONTH(posted)) PARTITIONS 12;
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
A possible workaround is as follows:
CREATE TABLE transactions (
id INT NOT NULL AUTO_INCREMENT,
description CHAR(100),
posted DATE,
PRIMARY KEY (id, posted)
) ENGINE=MyISAM
PARTITION BY HASH(MONTH(posted)) PARTITIONS 12;
Another workaround would be:
CREATE TABLE transactions (
id INT NOT NULL AUTO_INCREMENT,
description CHAR(100),
posted DATE,
KEY (id)
) ENGINE=MyISAM
PARTITION BY HASH(MONTH(posted)) PARTITIONS 12;
In both workarounds the database would not stop the situation of multiple records with the same id, but different posted dates. Is there any way to use partitioning on the posted field and maintain the original unique constraints?
I've been facing this same "problem", and one workaround that I found for that was splitting my table in two, resulting in something like this in your case:
CREATE TABLE transactions (
id INT NOT NULL AUTO_INCREMENT,
description CHAR(100),
PRIMARY KEY (id)
) ENGINE=MyISAM;
And the other:
CREATE TABLE transactions_date (
id INT NOT NULL,
posted DATE
) ENGINE=MyISAM
PARTITION BY HASH(MONTH(posted)) PARTITIONS 12;
The obvious problem is that you have to add some extra logic to the application, like the need to fetch both tables to retrieve all the data when using SELECT statements. You could probably use triggers to help you with the tasks related to INSERT, UPDATE and DELETE.
Just a note: the only functions that can benefit from the use of partition pruning in DATE or DATETIME columns are: YEAR(), TO_DAYS() and TO_SECONDS() (this last one is only available since MySQL 5.5).
Question:
Is there a way to make the foreign ID point to something more generic than one specific table?
Details:
Often I run into the situation where I have several tables which have nothing to do with each other, but still need a common table (in below examples engine is innodb)
CREATE TABLE IF NOT EXISTS movies
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS books
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS songs
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS news_papers
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS scrolls
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS sumarian_wheat_tablets
(
id INT NOT NULL auto_increment,
name VARCHAR(100) NOT NULL ,
PRIMARY KEY(id)
);
Now I want to keep a record of every time each is viewed like so
CREATE TABLE IF NOT EXISTS movie_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES movies ( id ),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS book_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES books ( id ),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS song_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES songs ( id ),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS news_paper_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES news_papers ( id ),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS scroll_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES scrolls ( id ),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS sumarian_wheat_tablet_history
(
id INT NOT NULL auto_increment,
foreign_id INT NOT NULL ,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (foreign_id) REFERENCES sumarian_wheat_tablets ( id ),
PRIMARY KEY(id)
);
Is there a more correct way to handle such situations without making n new tables? I realize that I can make one history table and copy it over with CREATE TABLE...LIKE... but that still requires making n new tables, plus I have to go in and ALTER the foreign_id.
My first thought is just dump the fk reference and have one history table:
CREATE TABLE history(
base_table VARCHAR,
base_table_id INT,
view_date TIMESTAMP DEFAULT now()
);
But I assume you want the fk to maintain the integrity (question: is this really necessary, or can this be worked around?). I guess you could accomplish this by creating a table of "pks in use". For example:
create a table "keys" with columns id (autoincrement) and base_table_name
create a table "movies", where id is both pk and also a fk to "keys.id" (but not an autoincrement column)
add a "before insert" trigger to "movies" which inserts a record into "keys" returning the generated id to be used as the id for the "movie" record
create a history table with a fk to "keys"
create a "delete" trigger on "movies" which also removes the record from "keys" if you want the integrity maintained, or cascading deletes, etc
So the generated "id" is shared across many tables. There is a school of thought that suggests using a primary key unique across all relations within the database (an "enterprise key"), so it is not unprecedented. Instead of using sequences or autogenerated columns, sometimes a GUID or UUID is used.
This replaces extra history tables with triggers on each base table, which might not be a great thing, depending on your environment. I haven't done this myself, just throwing some thoughts out there, so take it for what its worth.
This depends on the record that you're keeping. If you just want to know hits, add one field to each table that is incremented each time your 'hit' criteria is met (ie, there is a read from a webpage). If you want to hold more information:
CREATE TABLE IF NOT EXISTS view_history
(
id INT NOT NULL,
table VARCHAR NOT NULL,
//other relevant stats to a given view, such as ip and so on.
)
The id and table form a composite key as to what table it refers to.
I don't think there is a way to specify more than one table on a single foreign key.
If you define a single history table, you cannot enforce referential integrity using a single foreign key. You could enforce it programmaticaly as explained here
This describes how to do it for other storage engines that do not support FKs, but could be used as a guide to implement what you need. It suggests creating triggers that will enforce same validations a foreign key would.
Other approach:
CREATE TABLE IF NOT EXISTS history
(
id INT NOT NULL auto_increment,
movie_id INT,
book_id INT,
song_id INT,
news_paper_id INT,
view_date TIMESTAMP DEFAULT now(),
FOREIGN KEY (movie_id) REFERENCES movie ( id ),
FOREIGN KEY (book_id) REFERENCES book ( id ),
FOREIGN KEY (song_id) REFERENCES song ( id ),
FOREIGN KEY (news_paper_id) REFERENCES news_paper ( id ),
PRIMARY KEY(id)
);
I am new to databases, but I have gone over some tutorials and learned most of the essentials (I am a long time programmer). However, I need some help getting around the limitations of relational databases. If I have a picture I can create a simple table for it as such
"CREATE TABLE picture(
file VARCHAR(150),
rating INT
)";
If I want to keep track of who rates for the picture, I can either hard code a preset number of voters like so (in this example 3 anonymous votes)
"CREATE TABLE picture(
file VARCHAR(150),
rating INT,
vote1 INT,
vote2 INT,
vote3 INT
)";
Or for an unlimited number I can create a new table as such
"CREATE TABLE ratingTemplate
(
rater INT,
rating INT
)";
But for every picture entry in the picture table I want to have a reference to this entire ratingTemplate table. Is there any proper way to use foreign keys to achieve this? Currently I am micromanaging it by creating new ratingTemplate tables and making sure to store their names in the corresponding picture table entry.
Yes, you would have to have a table that keeps references to picture table as well as user table.
CREATE TABLE IF NOT EXISTS PICTURE (
PICTURE_ID BIGINT NOT NULL AUTO_INCREMENT,
FILE VARCHAR(250),
PRIMARY KEY (PICTURE_ID)
);
CREATE TABLE IF NOT EXISTS USER (
USER_ID BIGINT NOT NULL AUTO_INCREMENT,
--....REST OF COLUMNS HERE...
PRIMARY KEY(USER_ID)
);
CREATE TABLE IF NOT EXISTS PICTURE_RATING (
RATING_ID BIGINT NOT NULL AUTO_INCREMENT,
PICTURE_ID BIGINT NOT NULL,
USER_ID BIGINT NOT NULL,
RATING DOUBLE,
PRIMARY KEY (RATING_ID),
FOREIGN KEY (PICTURE_ID) REFERENCES PICTURE(PICTURE_ID),
FOREIGN KEY (USER_ID) REFERENCES USER(USER_ID)
);
You have not identified the primary key for the 'picture' table, but as presented the primary key would appear to be the 'file' column. You would need to add a column to the 'ratingTemplate' table named 'file' that would also be be varchar(150. Your primary key in the 'ratingTemplate' would be a combination of 'file' and 'rater'. The 'file' column in the 'ratingTemplate' table would be a foriegn key to the 'file' column int he 'picture' table.
Sample Query:
SELECT picture.file, rater, rating
FROM picture INNER JOIN ratingTemplate ON picture.file = ratingTemplate.file
WHERE picture.file = 'filenameiwant'
Another approach would be to add a surrogate primary key to the 'picture' table.
Perhaps named 'FileId' as an integer. In that case, you would use the 'FileId' column in the 'ratingTemplate' as the foriegn key instead of 'file'. For large data sets this would execute faster and use less space.
Sample query:
FROM picture INNER JOIN ratingTemplate ON picture.FileId = ratingTemplate.FileId
WHERE picture.FileId = 257
I am trying to add a self relation in an existing Innodb table here is table structure
Table person
person_id int (10) primary key not null auto increment,
parent_id int (10) NULL default null,
name varchar(30)
When I use this command
ALTER TABLE `person` ADD FOREIGN KEY ( `parent_id` ) REFERENCES `person` (`person_id`) ON DELETE RESTRICT ON UPDATE RESTRICT ;
I get the error data type mismatch. I think this could be due to null values in parent_id. Is there any way to skip this check?
Thanks
person_id and parent_id need to be the exact same data type. For example, if person_id is INT UNSIGNED and parent_id is INT, then you can't create the foreign key.
Run this command and compare the data types of the two columns:
SHOW CREATE TABLE `person`\G