Referencing code value tables in SQL - mysql

I have just started working with MySQL and have a quick question. I would like to create a table "My_Table" that has a field "SEX". I would also like to create a table "SEX_Values" which has fields "CODE" and "VALUE" with 0 = male, 1 = female. so that it looks like this.
My_Table SEX_Values
+--------+ +--------+--------+
| SEX | | CODE | VALUE |
+--------+ +--------+--------+
| | | 0 | male |
+--------+ +--------+--------+
| 1 | female |
+--------+--------+
I would like to somehow put a constraint on the SEX field in My_Table so that the data that is inserted/imported into it must match one of the codes in the SEX_Values table and if it doesn't I would like to throw a warning, something like this.
My_Table SEX_Values
+--------+ +--------+--------+
| SEX |<reference>| CODE | VALUE |
+--------+ +--------+--------+
| 1 | >OK | 0 | male |
+--------+ +--------+--------+
| 0 | >OK | 1 | female |
+--------+ +--------+--------+
| 0 | >OK
+--------+
| 3 | >Throws Warning
+--------+
Any help would be greatly appreciated as I have not used SQL much before.

What you describe is known as a FOREIGN KEY.
Essentially, it ensures referential integrity - in other words, you can't insert anything that doesn't exist in the referenced table, nor can you delete anything from the referenced table that still exists in the main one.
So, in your case you couldn't insert Hermaphrodite into My_Table without it being present in SEX_Values, nor could you remove male from SEX_Values, if there were still a male in My_Table.

CREATE TABLE SEX_Values
(
`CODE` INT NOT NULL,
`VALUES` VARCHAR(10),
PRIMARY KEY (`CODE`) -- !!
);
CREATE TABLE My_Table
(
SEX INT,
FOREIGN KEY (SEX) REFERENCES SEX_VALUES(`CODE`) -- !!
);

Why not use an enum type?
You can create the table with
CREATE TABLE my_table(
sex enum("male", "female") NOT NULL
);
That way you can insert like so:
INSERT INTO my_table(`sex`) VALUES("male")
You have the syntactical advantage of using strings (I think this is a lot more clear than using numeric codes), MySQL will optimize the database as if you were using the codes, and it will enfor.

A very simple solution is to set the datatype of the sex column to bit, that accepts only 0, 1 or null values (MSDN ref.).
Set also the NOT NULL constraint in the filed definition, in order to exclude NULL values as well.

Related

MySQL: Adding column to existing table. Have the values ready, how do I enter them altogether?

I have table with data on old game characters. I'd like to add a gender column.
If I do
ALTER TABLE characters
ADD gender ENUM('m','f') AFTER char_name
then I get a column full of NULLs. How do I get the values in?
Using an INSERT statement tries to tag them all into new rows, instead of replacing the NULLs.
Using an UPDATE statement requires a new statement for every single entry.
Is there any way to just drop a "VALUES ('m'),('f'),('f'),('m'),('f') etc" into the ALTER statement or anything else and update them all efficiently?
There is no way to fill in specific values during ALTER TABLE. The value will be NULL or else a default value you define for the column.
You may find INSERT...ON DUPLICATE KEY UPDATE is a convenient way to fill in the values.
Example:
CREATE TABLE characters (
id serial primary key,
char_name TEXT NOT NULL
);
INSERT INTO characters (char_name) VALUES
('Harry'), ('Ron'), ('Hermione');
SELECT * FROM characters;
+----+-----------+
| id | char_name |
+----+-----------+
| 1 | Harry |
| 2 | Ron |
| 3 | Hermione |
+----+-----------+
Now we add the gender column. It will add the new column with NULLs.
ALTER TABLE characters
ADD gender ENUM('m','f') AFTER char_name;
SELECT * FROM characters;
+----+-----------+--------+
| id | char_name | gender |
+----+-----------+--------+
| 1 | Harry | NULL |
| 2 | Ron | NULL |
| 3 | Hermione | NULL |
+----+-----------+--------+
Now we update the rows:
INSERT INTO characters (id, char_name, gender) VALUES
(1, '', 'm'), (2, '', 'm'), (3, '', 'f')
ON DUPLICATE KEY UPDATE gender = VALUES(gender);
It looks strange to use '' for the char_name, but it will be ignored anyway, because we don't set it in the ON DUPLICATE KEY clause. The original char_name is preserved. Specifying the value in the INSERT is necessary only because the column is defined NOT NULL and has no DEFAULT value.
SELECT * FROM characters;
+----+-----------+--------+
| id | char_name | gender |
+----+-----------+--------+
| 1 | Harry | m |
| 2 | Ron | m |
| 3 | Hermione | f |
+----+-----------+--------+
DBFiddle

What is the difference between LIKE and AS SELECT * FROM in duplicating a table in mysql?

I'm trying to practice sql using w3resource's sample activities. They gave an answer to that activity but they do not have further explanation on to why they used a specific script, and I want to know what would be the difference when I would do this.
Write a SQL statement to create the structure of a table dup_countries similar to countries.
Answer:
CREATE TABLE IF NOT EXISTS dup_countries LIKE countries;
Output:
mysql> DESC dup_countries;
+--------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------------+------+-----+---------+-------+
| COUNTRY_ID | varchar(2) | YES | | NULL | |
| COUNTRY_NAME | varchar(40) | YES | | NULL | |
| REGION_ID | decimal(10,0) | YES | | NULL | |
+--------------+---------------+------+-----+---------+-------+
3 rows in set (0.03 sec)
Write a SQL statement to create a duplicate copy of countries table including structure and data by name dup_countries.
Answer:
CREATE TABLE IF NOT EXISTS dup_countries AS SELECT * FROM countries;
Output:
mysql> DESC dup_countries;
+--------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------------+------+-----+---------+-------+
| COUNTRY_ID | varchar(2) | YES | | NULL | |
| COUNTRY_NAME | varchar(40) | YES | | NULL | |
| REGION_ID | decimal(10,0) | YES | | NULL | |
+--------------+---------------+------+-----+---------+-------+
3 rows in set (0.11 sec)
You can of course check the documentation (CREATE TABLE ... LIKE Statement, CREATE TABLE ... SELECT Statement) but there're two main differences:
CREATE TABLE LIKE creates an empty table, while CREATE TABLE SELECT inserts selected rows.
CREATE TABLE LIKE copies source table definitions, indexes included, while
CREATE TABLE SELECT figures out column types from actual data found (you don't necessarily have a source table to begin with, it can be any kind of dynamically generated result-set) and also allows you set the types manually.
We can Clone Table using two ways:
using Like
using As Select * from TableName.
1st Way:
CREATE TABLE new_table AS SELECT * FROM original_table;
It inherits only basic definitions,null settings and default values.
But doesnt inherit indexes and auto increment definitions.
It copy just structure and data.
2nd Way:
CREATE TABLE new_table LIKE original_table;
Inherit all table definitions without Data.
If we wants Data,we can use
INSERT INTO new_table SELECT * FROM original_table;
CREATE TABLE IF NOT EXISTS dup_countries LIKE countries;
is not equal to
CREATE TABLE IF NOT EXISTS dup_countries AS SELECT * FROM countries;
The main difference - 2nd query adds rows into newly created table whereas 1nd one creates empty table.
You may eliminate this difference by adding a condition to 2nd query:
CREATE TABLE IF NOT EXISTS dup_countries AS SELECT * FROM countries WHERE FALSE;
Both 1st and 3rd query produces the same result - empty table.
But the differences exists nevertheless. And they may be critical.
1st query copies a lot of constructions into the table-copy. Indices. Constraints (including NOT NULL, PRIMARY KEY...). Comments. And so on... And even generated columns definitions.
Whereas 2nd and 3rd queries creates "cleared" structure - only base properties are reproduced.
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=7bbebd3667bd333f1ca55edfeac04ae8
PS. And none query copies foreign keys and triggers.

The Foreign Key Options, ON DELETE CASCADE not working?

I am trying to use ON DELETE CASCADE for a database I'm working on. Didn't seem to work so I tested it out on a simple example with no success.
CREATE TABLE foo (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
data VARCHAR(10),
PRIMARY KEY (id)
)ENGINE=InnoDB;
CREATE TABLE foo2 (
id INT UNSIGNED NOT NULL,
data2 VARCHAR(10),
PRIMARY KEY (id),
CONSTRAINT fk_foo2_id FOREIGN KEY (id) REFERENCES foo(id) ON DELETE CASCADE
)ENGINE=InnoDB;
INSERT INTO foo (data) VALUE ('hello'),('world'),('mysql');
INSERT INTO foo2 (data2) VALUE ('hello2'),('world2'),('mysql2');
SELECT * FROM foo;
+----+-------+
| id | data |
+----+-------+
| 1 | hello |
| 2 | world |
| 3 | mysql |
+----+-------+
3 rows in set (0.00 sec)
SELECT * FROM foo2;
+----+--------+
| id | data2 |
+----+--------+
| 1 | hello2 |
| 2 | world2 |
| 3 | mysql2 |
+----+--------+
3 rows in set (0.00 sec)
DELETE FROM foo WHERE id=2;
SELECT * FROM foo;
+----+-------+
| id | data |
+----+-------+
| 1 | hello |
| 3 | mysql |
+----+-------+
2 rows in set (0.00 sec)
SELECT * FROM foo2;
+----+--------+
| id | data2 |
+----+--------+
| 1 | hello2 |
| 2 | world2 |
| 3 | mysql2 |
+----+--------+
3 rows in set (0.00 sec)
I can't for the life of me figure out why this isn't working. I looked at similar questions and answers on here and I did exactly what they said and it still didn't work. Most of them just said to change to ENGINE=InnoDb, but I tried it and no success.
There must be something I'm missing here, and it's probably very obvious.. Monday mornings.
If anyone can shed some light on this little noob problem of mine, I would greatly appreciate it!
Edit: removed the auto_increment from id in foo2 as it did not belong there
The first thing that pops to mind is to check the setting of the foreign_key_checks variable. If that's set to 0 (FALSE), then foreign key constraints are NOT enforced.
SHOW VARIABLES LIKE 'foreign_key_checks'
To enable foeign key constraints, set to the variable to 1
SET foreign_key_checks = 1;
NOTE: this affects only the current session. New sessions inherit the GLOBAL setting.
Also, verify that your tables are actually using the InnoDB engine, and that the foreign keys are defined. Easiest way is to get the output from:
SHOW CREATE TABLE foo;
SHOW CREATE TABLE foo2;
FOLLOWUP
This is something that we expect NOT to be broken in MySQL 5.1.61.
As a workaround, try defining the foreign key constraint as a separate ALTER TABLE statement.
ALTER TABLE foo2
ADD CONSTRAINT fk_foo2_id FOREIGN KEY (id) REFERENCES foo(id) ON DELETE CASCADE ;
I don't see much use in a foreign key constraint between two columns that are both defined with "auto_increment". In your example, you could easily create several rows in table "foo" (without a counterpart in "foo2"), and from then onwards you could not control whether "id" values in both tables match.
I admit I didn't check the documentation, but it would not surprise me if MySQL silently ignored a foreign key constraint for an auto-generated column.
IMNSHO, your table "foo2" should use "id" values which are set explicitly and reference specific rows in "foo", because then it would make sense that deleting such "foo" rows should cascade onto "foo2".

Adding auto increment column other than primary key

I have table in which has key is primary key. I want to add seqNo key which should be auto incremented but it does not allow to make it as auto increment.
Because there is already one primary key,
Is it possible to make seqNo auto increment? currently seqNo is not present. I want to add it
You can't have two identity columns in a SQL table but you can still create sequences. Here's the link http://technet.microsoft.com/en-us/library/ff878091.aspx
You have the following options.
Make a trigger that increments your column value on every insert statement
Use a sequence, but once a sequence value is generated it will never be generated again (meaning, you would get a gap in your values if your insert fails for some reason)
Vignesh, consider the following...
DROP TABLE IF EXISTS test;
CREATE TABLE test
( testID int(11) NOT NULL
, string varchar(45) DEFAULT NULL
, testInc int(11) NOT NULL AUTO_INCREMENT
, PRIMARY KEY (testID)
, KEY testInc (testInc)
);
INSERT INTO test
(testID
, string
) values
(1
,'Hello'
);
INSERT INTO test (testid,string) SELECT x.testid + y.max_test,string FROM test x JOIN (SELECT MAX(testid) max_test FROM test)y;
INSERT INTO test (testid,string) SELECT x.testid + y.max_test,string FROM test x JOIN (SELECT MAX(testid) max_test FROM test)y;
INSERT INTO test (testid,string) SELECT x.testid + y.max_test,string FROM test x JOIN (SELECT MAX(testid) max_test FROM test)y;
Query OK, 4 rows affected (0.03 sec)
SELECT * FROM test;
+--------+--------+---------+
| testID | string | testInc |
+--------+--------+---------+
| 1 | Hello | 1 |
| 2 | Hello | 2 |
| 3 | Hello | 3 |
| 4 | Hello | 4 |
| 5 | Hello | 6 |
| 6 | Hello | 7 |
| 7 | Hello | 8 |
| 8 | Hello | 9 |
+--------+--------+---------+
Note that the number of rows (8), and the value of testinc (9) are different. This is not what the OP wants. The MAX() trick I've used for generating the PK is also no good, because it's subject to runtime errors.
fiddle of same http://www.sqlfiddle.com/#!2/d29a5b/1
The point is... storing a sequential id is pointless.

MySQL update to other tables

I want to be able to insert data into t1 and have data get populated in table t2 with the primary key as a foreign key in t2.
Basically, how come in my current setup when I INSERT INTO t1 (first_name, last_name) values ( "blah", "blah"); and then do SELECT * FROM t2; t2 it says Empty Set (0.00 sec) for t2? Shouldn't it at least show the default id of 1?
t1:
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| first_name | varchar(20) | NO | | NULL | |
| last_name | varchar(20) | NO | | NULL | |
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
+------------+------------------+------+-----+---------+----------------+
t2:
+-----------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+-------+
| address | varchar(50) | NO | | NULL | |
| id | int(10) unsigned | NO | MUL | NULL | |
| last_name | varchar(20) | YES | | NULL | |
+-----------+------------------+------+-----+---------+-------+
In a relational database, a FOREIGN KEY is a declaration that you intend to insert values into T2 that must match an already existing value in T1, and that you want the database to refuse to perform any action that would break this relationship.
It does not mean that the database will create records on its own in order to satisfy a relationship. If you try to insert a value into T2 that does not exist in T1, the command will fail; it will not add the required record to T1.
That is the opposite of what you're suggesting, however, in which you want the foreign key values to get automatically generated. However, there's no requirement that a primary key value actually have references and, furthermore, no limit on the number of times that primary key value can be referenced — so how would the database guess what should be created in T2?
That said, if you want some of your own code to execute automatically when data is added to T1, code which can do whatever you want, you can create a trigger on T1.
No, tables won't propagate automatically. (You can however do it with triggers) You will have to insert into t2.
You can create a trigger on table t1 so that it inserts a row into t2 with the correct id and the other fields NULL
Foreign keys will not insert records for you.
DELIMITER ;;
CREATE TRIGGER insert_addr_rec BEFORE INSERT ON t1
FOR EACH ROW BEGIN
INSERT INTO t2 SET id=NEW.id, last_name=NEW.last_name
END ;;
DELIMITER ;
NB untested code