MySQL by Examples For Beginners
MySQL by Examples For Beginners
Read "How to Install MySQL and Get Started" on how to install, customize, and get started with MySQL.
1. Summary of MySQL Commands Used in this Tutorial
For detailed syntax, check MySQL manual "SQL Statement Syntax"
@ http://dev.mysql.com/doc/refman/5.5/en/sql-syntax.html.
-- Database-Level
DROP DATABASE databaseName -- Delete the database (irrecoverable!)
DROP DATABASE IF EXISTS databaseName -- Delete if it exists
CREATE DATABASE databaseName -- Create a new database
CREATE DATABASE IF NOT EXISTS databaseName -- Create only if it does not exists
SHOW DATABASES -- Show all the databases in this server
USE databaseName -- Set the default (current) database
SELECT DATABASE() -- Show the default database
SHOW CREATE DATABASE databaseName -- Show the CREATE DATABASE statement
-- Table-Level
DROP TABLE [IF EXISTS] tableName, ...
CREATE TABLE [IF NOT EXISTS] tableName (
columnName columnType columnAttribute, ...
PRIMARY KEY(columnName),
FOREIGN KEY (columnNmae) REFERENCES tableName (columnNmae)
)
SHOW TABLES -- Show all the tables in the default database
DESCRIBE|DESC tableName -- Describe the details for a table
ALTER TABLE tableName ... -- Modify a table, e.g., ADD COLUMN and DROP COLUMN
ALTER TABLE tableName ADD columnDefinition
ALTER TABLE tableName DROP columnName
ALTER TABLE tableName ADD FOREIGN KEY (columnNmae) REFERENCES tableName (columnNmae)
ALTER TABLE tableName DROP FOREIGN KEY constraintName
SHOW CREATE TABLE tableName -- Show the CREATE TABLE statement for this tableName
-- Row-Level
INSERT INTO tableName
VALUES (column1Value, column2Value,...) -- Insert on all Columns
INSERT INTO tableName
VALUES (column1Value, column2Value,...), ... -- Insert multiple rows
INSERT INTO tableName (column1Name, ..., columnNName)
VALUES (column1Value, ..., columnNValue) -- Insert on selected Columns
DELETE FROM tableName WHERE criteria
UPDATE tableName SET columnName = expr, ... WHERE criteria
SELECT * | column1Name AS alias1, ..., columnNName AS aliasN
FROM tableName
WHERE criteria
GROUP BY columnName
ORDER BY columnName ASC|DESC, ...
HAVING groupConstraints
LIMIT count | offset count
-- Others
SHOW WARNINGS; -- Show the warnings of the previous statement
-- Create the table "products". Read "explanations" below for the column defintions
mysql> CREATE TABLE IF NOT EXISTS products (
productID INT UNSIGNED NOT NULL AUTO_INCREMENT,
productCode CHAR(3) NOT NULL DEFAULT '',
name VARCHAR(30) NOT NULL DEFAULT '',
quantity INT UNSIGNED NOT NULL DEFAULT 0,
price DECIMAL(7,2) NOT NULL DEFAULT 99999.99,
PRIMARY KEY (productID)
);
Query OK, 0 rows affected (0.08 sec)
-- Show all the tables to confirm that the "products" table has been created
mysql> SHOW TABLES;
+---------------------+
| Tables_in_southwind |
+---------------------+
| products |
+---------------------+
-- Show the complete CREATE TABLE statement used by MySQL to create this table
mysql> SHOW CREATE TABLE products \G
*************************** 1. row ***************************
Table: products
Create Table:
CREATE TABLE `products` (
`productID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`productCode` char(3) NOT NULL DEFAULT '',
`name` varchar(30) NOT NULL DEFAULT '',
`quantity` int(10) unsigned NOT NULL DEFAULT '0',
`price` decimal(7,2) NOT NULL DEFAULT '99999.99',
PRIMARY KEY (`productID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Explanations
We define 5 columns in the table products: productID, productCode, name, quantity and price.
The types are:
productID is INT UNSIGNED - non-negative integers.
productCode is CHAR(3) - a fixed-length alphanumeric string of 3 characters.
name is VARCHAR(30) - a variable-length string of up to 30 characters.
We use fixed-length string for productCode, as we assume that
the productCode contains exactly 3 characters. On the other hand, we use variable-length string
for name, as its length varies - VARCHAR is more efficient than CHAR.
quantity is also INT UNSIGNED (non-negative integers).
price is DECIMAL(10,2) - a decimal number with 2 decimal places.
DECIMAL is precise (represented as integer with a fix decimal point). On the other
hand, FLOAT and DOUBLE (real numbers) are not precise and are approximated. DECIMAL type is
recommended for currency.
The attribute "NOT NULL" specifies that the column cannot contain the NULL value. NULL is a special value
indicating "no value", "unknown value" or "missing value". In our case, these columns shall have a proper
value. We also set the default value of the columns. The column will take on its default value, if no value is
specified during the record creation.
We set the column productID as the so-called primary key. Values of the primary-key column must be
unique. Every table shall contain a primary key. This ensures that every row can be distinguished from other
rows. You can specify a single column or a set of columns (e.g., firstName and lastName) as the primary
key. An index is build automatically on the primary-key column to facilitate fast search. Primary key is also
used as reference by other tables.
We set the column productID to AUTO_INCREMENT. with default starting value of 1. When you insert a
row with NULL (recommended) (or 0, or a missing value) for the AUTO_INCREMENT column, the maximum
value of that column plus 1 would be inserted. You can also insert a valid value to
an AUTO_INCREMENT column, bypassing the auto-increment.
2.4 Inserting Rows - INSERT INTO
Let's fill up our "products" table with rows. We set the productID of the first record to 1001, and
use AUTO_INCREMENT for the rest of records by inserting a NULL, or with a missing column value. Take
note that strings must be enclosed with a pair of single quotes (or double quotes).
-- Insert a row with all the column values
mysql> INSERT INTO products VALUES (1001, 'PEN', 'Pen Red', 5000, 1.23);
Query OK, 1 row affected (0.04 sec)
-- List all the rows of ALL columns, * is a wildcard denoting all columns
SELECT * FROM tableName
For examples,
-- List all rows of ALL the columns. The wildcard * denotes ALL columns
mysql> SELECT * FROM products;
+-----------+-------------+-----------+----------+-------+
| productID | productCode | name | quantity | price |
+-----------+-------------+-----------+----------+-------+
| 1001 | PEN | Pen Red | 5000 | 1.23 |
| 1002 | PEN | Pen Blue | 8000 | 1.25 |
| 1003 | PEN | Pen Black | 2000 | 1.25 |
| 1004 | PEC | Pencil 2B | 10000 | 0.48 |
| 1005 | PEC | Pencil 2H | 8000 | 0.49 |
+-----------+-------------+-----------+----------+-------+
5 rows in set (0.00 sec)
SELECT without Table
You can also issue SELECT without a table. For example, you can SELECT an expression or evaluate a built-
in function.
mysql> SELECT 1+1;
+-----+
| 1+1 |
+-----+
| 2 |
+-----+
1 row in set (0.00 sec)
// Multiple columns
mysql> SELECT 1+1, NOW();
+-----+---------------------+
| 1+1 | NOW() |
+-----+---------------------+
| 2 | 2012-10-24 22:16:34 |
+-----+---------------------+
1 row in set (0.00 sec)
Comparison Operators
For numbers (INT, DECIMAL, FLOAT), you could use comparison operators: '=' (equal to), '<>' or '!
=' (not equal to), '>' (greater than), '<' (less than), '>=' (greater than or equal to), '<=' (less than or
equal to), to compare two numbers. For example, price > 1.0, quantity <= 500.
mysql> SELECT name, price FROM products WHERE price < 1.0;
+-----------+-------+
| name | price |
+-----------+-------+
| Pencil 2B | 0.48 |
| Pencil 2H | 0.49 |
+-----------+-------+
2 rows in set (0.00 sec)
mysql> SELECT name, quantity FROM products WHERE quantity <= 2000;
+-----------+----------+
| name | quantity |
+-----------+----------+
| Pen Black | 2000 |
+-----------+----------+
1 row in set (0.00 sec)
CAUTION: Do not compare FLOATs (real numbers) for equality ('=' or '<>'), as they are not precise. On
the other hand, DECIMAL are precise.
For strings, you could also use '=', '<>', '>', '<', '>=', '<=' to compare two strings
(e.g., productCode = 'PEC'). The ordering of string depends on the so-called collation chosen. For
example,
mysql> SELECT name, price FROM products WHERE productCode = 'PEN';
-- String values are quoted
+-----------+-------+
| name | price |
+-----------+-------+
| Pen Red | 1.23 |
| Pen Blue | 1.25 |
| Pen Black | 1.25 |
+-----------+-------+
3 rows in set (0.00 sec)
String Pattern Matching - LIKE and NOT LIKE
For strings, in addition to full matching using operators like '=' and '<>', we can perform pattern
matching using operator LIKE (or NOT LIKE) with wildcard characters. The wildcard '_' matches any
single character; '%' matches any number of characters (including zero). For example,
'abc%' matches strings beginning with 'abc';
'%xyz' matches strings ending with 'xyz';
'%aaa%' matches strings containing 'aaa';
'___' matches strings containing exactly three characters; and
'a_b%' matches strings beginning with 'a', followed by any single character, followed by 'b',
followed by zero or more characters.
-- "name" begins with 'PENCIL'
mysql> SELECT name, price FROM products WHERE name LIKE 'PENCIL%';
+-----------+-------+
| name | price |
+-----------+-------+
| Pencil 2B | 0.48 |
| Pencil 2H | 0.49 |
+-----------+-------+
+ Addition
- Subtraction
* Multiplication
/ Division
DIV Integer Division
% Modulus (Remainder)
Logical Operators - AND, OR, NOT, XOR
You can combine multiple conditions with boolean operators AND, OR, XOR. You can also invert a condition
using operator NOT. For examples,
mysql> SELECT * FROM products WHERE quantity >= 5000 AND name LIKE 'Pen %';
+-----------+-------------+----------+----------+-------+
| productID | productCode | name | quantity | price |
+-----------+-------------+----------+----------+-------+
| 1001 | PEN | Pen Red | 5000 | 1.23 |
| 1002 | PEN | Pen Blue | 8000 | 1.25 |
+-----------+-------------+----------+----------+-------+
mysql> SELECT * FROM products WHERE quantity >= 5000 AND price < 1.24 AND name LIKE 'Pen %';
+-----------+-------------+---------+----------+-------+
| productID | productCode | name | quantity | price |
+-----------+-------------+---------+----------+-------+
| 1001 | PEN | Pen Red | 5000 | 1.23 |
+-----------+-------------+---------+----------+-------+
mysql> SELECT * FROM products WHERE NOT (quantity >= 5000 AND name LIKE 'Pen %');
+-----------+-------------+-----------+----------+-------+
| productID | productCode | name | quantity | price |
+-----------+-------------+-----------+----------+-------+
| 1003 | PEN | Pen Black | 2000 | 1.25 |
| 1004 | PEC | Pencil 2B | 10000 | 0.48 |
| 1005 | PEC | Pencil 2H | 8000 | 0.49 |
+-----------+-------------+-----------+----------+-------+
IN, NOT IN
You can select from members of a set with IN (or NOT IN) operator. This is easier and clearer than the
equivalent AND-OR expression.
mysql> SELECT * FROM products WHERE name IN ('Pen Red', 'Pen Black');
+-----------+-------------+-----------+----------+-------+
| productID | productCode | name | quantity | price |
+-----------+-------------+-----------+----------+-------+
| 1001 | PEN | Pen Red | 5000 | 1.23 |
| 1003 | PEN | Pen Black | 2000 | 1.25 |
+-----------+-------------+-----------+----------+-------+
BETWEEN, NOT BETWEEN
To check if the value is within a range, you could use BETWEEN ... AND ... operator. Again, this is
easier and clearer than the equivalent AND-OR expression.
mysql> SELECT
productCode AS `Product Code`,
COUNT(*) AS `Count`,
CAST(AVG(price) AS DECIMAL(7,2)) AS `Average`
FROM products
GROUP BY productCode
HAVING Count >=3;
-- CANNOT use WHERE count >= 3
+--------------+-------+---------+
| Product Code | Count | Average |
+--------------+-------+---------+
| PEN | 3 | 1.24 |
+--------------+-------+---------+
WITH ROLLUP
The WITH ROLLUP clause shows the summary of group summary, e.g.,
mysql> SELECT
productCode,
MAX(price),
MIN(price),
CAST(AVG(price) AS DECIMAL(7,2)) AS `Average`,
SUM(quantity)
FROM products
GROUP BY productCode
WITH ROLLUP; -- Apply aggregate functions to all groups
+-------------+------------+------------+---------+---------------+
| productCode | MAX(price) | MIN(price) | Average | SUM(quantity) |
+-------------+------------+------------+---------+---------------+
| PEC | 0.49 | 0.48 | 0.49 | 18000 |
| PEN | 1.25 | 1.23 | 1.24 | 15000 |
| NULL | 1.25 | 0.48 | 0.94 | 33000 |
+-------------+------------+------------+---------+---------------+
For example,
For example,
-- Use this with extreme care, as the deleted records are irrecoverable!
mysql> DELETE FROM products;
Query OK, 3 rows affected (0.00 sec)
Beware that "DELETE FROM tableName" without a WHERE clause deletes ALL records from the table.
Even with a WHERE clause, you might have deleted some records unintentionally. It is always advisable to
issue a SELECT command with the same WHERE clause to check the result set before issuing
the DELETE (and UPDATE).
2.9 Loading/Exporting Data from/to a Text File
There are several ways to add data into the database: (a) manually issue the INSERT commands; (b) run
the INSERT commands from a script; or (c) load raw data from a file using LOAD DATA or
via mysqlimport utility.
LOAD DATA LOCAL INFILE ... INTO TABLE ...
Besides using INSERT commands to insert rows, you could keep your raw data in a text file, and load them
into the table via the LOAD DATA command. For example, use a text editor to CREATE a NEW FILE called
"products_in.csv", under "d:\myProject" (for Windows) or " Documents" (for Mac), containing the
following records, where the values are separated by ','. The file extension of ".csv" stands for Comma-
Separated Values text file.
\N,PEC,Pencil 3B,500,0.52
\N,PEC,Pencil 4B,200,0.62
\N,PEC,Pencil 5B,100,0.73
\N,PEC,Pencil 6B,500,0.47
You can load the raw data into the products table as follows:
(For Windows)
-- Need to use forward-slash (instead of back-slash) as directory separator
mysql> LOAD DATA LOCAL INFILE 'd:/myProject/products_in.csv' INTO TABLE products
COLUMNS TERMINATED BY ','
LINES TERMINATED BY '\r\n';
(For Macs)
mysql> LOAD DATA LOCAL INFILE '~/Documents/products_in.csv' INTO TABLE products
COLUMNS TERMINATED BY ',';
mysql> SELECT * FROM products;
+-----------+-------------+-----------+----------+-------+
| productID | productCode | name | quantity | price |
+-----------+-------------+-----------+----------+-------+
| 1007 | PEC | Pencil 3B | 500 | 0.52 |
| 1008 | PEC | Pencil 4B | 200 | 0.62 |
| 1009 | PEC | Pencil 5B | 100 | 0.73 |
| 1010 | PEC | Pencil 6B | 500 | 0.47 |
+-----------+-------------+-----------+----------+-------+
Notes:
You need to provide the path (absolute or relative) and the filename. Use Unix-style forward-
slash '/' as the directory separator, instead of Windows-style back-slash '\'.
The default line delimiter (or end-of-line) is '\n' (Unix-style). If the text file is prepared in Windows,
you need to include LINES TERMINATED BY '\r\n'.
The default column delimiter is "tab" (in a so-called TSV file - Tab-Separated Values). If you use another
delimiter, e.g. ',', include COLUMNS TERMINATED BY ','.
You need to use \N for NULL.
mysqlimport Utility Program
You can also use the mysqlimport utility program to load data from a text file.
-- SYNTAX
> mysqlimport -u username -p --local databaseName tableName.tsv
-- The raw data must be kept in a TSV (Tab-Separated Values) file with filename the same as
tablename
-- EXAMPLES
-- Create a new file called "products.tsv" containing the following record,
-- and saved under "d:\myProject" (for Windows) or "Documents" (for Mac)
-- The values are separated by tab (not spaces).
\N PEC Pencil 3B 500 0.52
\N PEC Pencil 4B 200 0.62
\N PEC Pencil 5B 100 0.73
\N PEC Pencil 6B 500 0.47
(For Windows)
> cd path-to-mysql-bin
> mysqlimport -u root -p --local southwind d:/myProject/products.tsv
(For Macs)
$ cd /usr/local/mysql/bin
$ ./mysqlimport -u root -p --local southwind ~/Documents/products.tsv
SELECT ... INTO OUTFILE ...
Complimenting LOAD DATA command, you can use SELECT ... INTO
OUTFILE fileName FROM tableName to export data from a table to a text file. For example,
(For Windows)
mysql> SELECT * FROM products INTO OUTFILE 'd:/myProject/products_out.csv'
COLUMNS TERMINATED BY ','
LINES TERMINATED BY '\r\n';
(For Macs)
mysql> SELECT * FROM products INTO OUTFILE '~/Documents/products_out.csv'
COLUMNS TERMINATED BY ',';
Database: southwind
Table: products
supp
productID productCode name quantity price
INT CHAR(3) VARCHAR(30) INT DECIMAL(10,2)
(Fore
2001 501
2002 501
2003 501
2004 502
2001 503
Database: southwind
Table: suppliers
supplierID name phone
INT VARCHAR(30) CHAR(8)
Database: southwind
Table: products
productID productCode name quantity price
INT CHAR(3) VARCHAR(30) INT DECIMAL(10,2)
(For Macs)
-- Start a NEW "terminal"
$ cd /usr/local/mysql/bin
$ ./mysqldump -u root -p --databases southwind > ~/Documents/backup_southwind.sql
Study the output file, which contains CREATE DATABASE, CREATE TABLE and INSERT statements to re-
create the tables dumped.
The SYNTAX for the mysqldump utility program is as follows:
-- Dump selected databases with --databases option
> mysqldump -u username -p --databases database1Name [database2Name ...] > backupFile.sql
-- Dump all databases in the server with --all-databases option, except mysql.user table (for
security)
> mysqldump -u root -p --all-databases --ignore-table=mysql.user > backupServer.sql
We define the foreign key when defining the child table, which references a parent table, as follows:
-- Try deleting a row from parent table with matching rows in the child table
mysql> DELETE FROM suppliers WHERE supplierID = 501;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails
(`southwind`.`products_suppliers`, CONSTRAINT `products_suppliers_ibfk_2`
FOREIGN KEY (`supplierID`) REFERENCES `suppliers` (`supplierID`))
The record cannot be deleted as the default "ON DELETE RESTRICT" constraint was imposed.
4.3 Indexes (or Keys)
Indexes (or Keys) can be created on selected column(s) to facilitate fast search. Without index, a "SELECT
* FROM products WHERE productID=x" needs to match with the productID column of all the
records in the products table. If productID column is indexed (e.g., using a binary tree), the matching
can be greatly improved (via the binary tree search).
You should index columns which are frequently used in the WHERE clause; and as JOIN columns.
The drawback about indexing is cost and space. Building and maintaining indexes require computations and
memory spaces. Indexes facilitate fast search but deplete the performance on modifying the table
(INSERT/UPDATE/DELETE), and need to be justified. Nevertheless, relational databases are typically
optimized for queries and retrievals, but NOT for updates.
In MySQL, the keyword KEY is synonym to INDEX.
There can be more than one indexes in a table. Index are automatically built on the primary-key column(s).
You can build index via CREATE TABLE, CREATE INDEX or ALTER TABLE.
CREATE TABLE tableName (
......
[UNIQUE] INDEX|KEY indexName (columnName, ...),
-- The optional keyword UNIQUE ensures that all values in this column are distinct
-- KEY is synonym to INDEX
......
PRIMAY KEY (columnName, ...) -- Index automatically built on PRIMARY KEY column
);
5. More SQL
5.1 Sub-Query
Results of one query can be used in another SQL statement. Subquery is useful if more than one tables are
involved.
SELECT with Subquery
In the previous many-to-many product sales example, how to find the suppliers that do not supply any
product? You can query for the suppliers that supply at least one product in
the products_suppliers table, and then query the suppliers table for those that are not in the
previous result set.
mysql> SELECT suppliers.name from suppliers
WHERE suppliers.supplierID
NOT IN (SELECT DISTINCT supplierID from products_suppliers);
With date/time data types, you can sort the results by date, search for a particular date or a range of dates,
calculate the difference between dates, compute a new date by adding/subtracting an interval from a given
date.
Date By Example
Let's begin with Date (without Time) with the following example. Take note that date value must be written
as a string in the format of 'yyyy-mm-dd', e.g., '2012-01-31'.
-- Create a table 'patients' of a clinic
mysql> CREATE TABLE patients (
patientID INT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(30) NOT NULL DEFAULT '',
dateOfBirth DATE NOT NULL,
lastVisitDate DATE NOT NULL,
nextVisitDate DATE NULL,
-- The 'Date' type contains a date value in 'yyyy-mm-dd'
PRIMARY KEY (patientID)
);
-- Select patients who were born in a particular year and sort by birth-month
-- Function YEAR(date), MONTH(date), DAY(date) returns
-- the year, month, day part of the given date
mysql> SELECT * FROM patients
WHERE YEAR(dateOfBirth) = 2011
ORDER BY MONTH(dateOfBirth), DAY(dateOfBirth);
+-----------+-------+-------------+---------------+---------------+
| patientID | name | dateOfBirth | lastVisitDate | nextVisitDate |
+-----------+-------+-------------+---------------+---------------+
| 1003 | Ali | 2011-01-30 | 2012-10-21 | NULL |
| 1002 | Kumar | 2011-10-29 | 2012-09-20 | NULL |
+-----------+-------+-------------+---------------+---------------+
For examples,
+-------------+--------------+------------+-------------+---------------+---------------+
Extracting
information: DAYNAME() (e.g., 'Monday'), MONTHNAME() (e.g., 'March'), DAYOFWEEK() (1=Sund
ay, …, 7=Saturday), DAYOFYEAR() (1-366), ...
mysql> SELECT DAYNAME(NOW()), MONTHNAME(NOW()), DAYOFWEEK(NOW()), DAYOFYEAR(NOW());
+----------------+------------------+------------------+------------------+
| DAYNAME(NOW()) | MONTHNAME(NOW()) | DAYOFWEEK(NOW()) | DAYOFYEAR(NOW()) |
+----------------+------------------+------------------+------------------+
| Wednesday | October | 4 | 298 |
+----------------+------------------+------------------+------------------+
2011-11-30
Computing
interval: DATEDIFF(end_date, start_date), TIMEDIFF(end_time, start_time
), TIMESTAMPDIFF(unit, start_timestamp, end_timestamp), e.g.,
mysql> SELECT DATEDIFF('2012-02-01', '2012-01-28');
4
mysql> SELECT TIMESTAMPDIFF(DAY, '2012-02-01', '2012-01-28');
-4
2012-02-01
+-------------+-------------+------+-----+---------------------
+-----------------------------+
Notes:
o Don't use year(2) anymore.
o From MySQL 5.7, the supported range for datetime is '1000-01-01 00:00:00' to '9999-
12-31 23:59:59'.
23. Insert values manually using string literals.
+--------------+---------------------+------------+----------+-------+--------
+---------------------+
34. Checking the on-update for TIMSTAMP.
35. mysql> UPDATE `datetime_arena` SET `cYear2`='99' WHERE description='Manual Entry';
36.
37. mysql> SELECT * FROM `datetime_arena` WHERE description='Manual Entry';
38. +--------------+---------------------+------------+----------+-------+--------
+---------------------+
39. | description | cDateTime | cDate | cTime | cYear | cYear2 | cTimeStamp
|
40. +--------------+---------------------+------------+----------+-------+--------
+---------------------+
41. | Manual Entry | 2001-01-01 23:59:59 | 2002-02-02 | 12:30:30 | 2004 | 99 | 2010-04-08
14:44:48 |
+--------------+---------------------+------------+----------+-------+--------
+---------------------+
42. Insert values using MySQL built-in functions now(), curdate(), curtime().
43. mysql> INSERT INTO `datetime_arena`
44. (`description`, `cDateTime`, `cDate`, `cTime`, `cYear`, `cYear2`)
45. VALUES
46. ('Built-in Functions', now(), curdate(), curtime(), now(), now());
47.
48. mysql> SELECT * FROM `datetime_arena` WHERE description='Built-in Functions';
49. +--------------------+---------------------+------------+----------+-------+--------
+---------------------+
50. | description | cDateTime | cDate | cTime | cYear | cYear2 |
cTimeStamp |
51. +--------------------+---------------------+------------+----------+-------+--------
+---------------------+
52. | Built-in Functions | 2010-04-08 14:45:48 | 2010-04-08 | 14:45:48 | 2010 | 10 |
2010-04-08 14:45:48 |
+--------------------+---------------------+------------+----------+-------+--------
+---------------------+
53. Insert invalid or out-of-range values. MySQL replaces with all zeros.
+-------------+---------------------+------------+----------+-------+--------
+---------------------+
Note: Might not work in MySQL 5.7?!
64. An useful built-in function INTERVAL can be used to compute a future date, e.g.,
65. mysql> SELECT `cDate`, `cDate` + INTERVAL 30 DAY, `cDate` + INTERVAL 1 MONTH FROM
`datetime_arena`;
66. +------------+---------------------------+----------------------------+
67. | cDate | `cDate` + INTERVAL 30 DAY | `cDate` + INTERVAL 1 MONTH |
68. +------------+---------------------------+----------------------------+
69. | 2002-02-02 | 2002-03-04 | 2002-03-02 |
70. | 2010-04-08 | 2010-05-08 | 2010-05-08 |
71. | 0000-00-00 | NULL | NULL |
+------------+---------------------------+----------------------------+
5.3 View
A view is a virtual table that contains no physical data. It provide an alternative way to look at the data.
Example
-- Define a VIEW called supplier_view from products, suppliers and products_suppliers tables
mysql> CREATE VIEW supplier_view
AS
SELECT suppliers.name as `Supplier Name`, products.name as `Product Name`
FROM products
JOIN suppliers ON products.productID = products_suppliers.productID
JOIN products_suppliers ON suppliers.supplierID = products_suppliers.supplierID;
5.4 Transactions
A atomic transaction is a set of SQL statements that either ALL succeed or ALL fail. Transaction is important
to ensure that there is no partial update to the database, given an atomic of SQL statements. Transactions
are carried out via COMMIT and ROLLBACK.
Example
mysql> UPDATE accounts SET balance = balance - 100 WHERE name = 'Paul';
mysql> UPDATE accounts SET balance = balance + 100 WHERE name = 'Peter';
mysql> ROLLBACK;
mysql> SELECT * FROM accounts;
+-------+---------+
| name | balance |
+-------+---------+
| Paul | 800.00 |
| Peter | 2200.00 |
+-------+---------+
6. More on JOIN
6.1 INNER JOIN
In an inner join of two tables, each row of the first table is combined (joined) with every row of second table.
Suppose that there are n1 rows in the first table and n2 rows in the second table, INNER JOIN produces all
combinations of n1×n2 rows - it is known as Cartesian Product or Cross Product.
Example
mysql> DROP TABLE IF EXISTS t1, t2;
mysql> SELECT *
FROM t1 INNER JOIN t2;
+----+------------+----+------------+
| id | desc | id | desc |
+----+------------+----+------------+
| 1 | ID 1 in t1 | 2 | ID 2 in t2 |
| 2 | ID 2 in t1 | 2 | ID 2 in t2 |
| 3 | ID 3 in t1 | 2 | ID 2 in t2 |
| 1 | ID 1 in t1 | 3 | ID 3 in t2 |
| 2 | ID 2 in t1 | 3 | ID 3 in t2 |
| 3 | ID 3 in t1 | 3 | ID 3 in t2 |
| 1 | ID 1 in t1 | 4 | ID 4 in t2 |
| 2 | ID 2 in t1 | 4 | ID 4 in t2 |
| 3 | ID 3 in t1 | 4 | ID 4 in t2 |
+----+------------+----+------------+
-- SELECT all columns in t1 and t2 (*)
-- INNER JOIN produces ALL combinations of rows in t1 and t2
You can impose constrain by using the ON clause, for example,
mysql> SELECT *
FROM t1 INNER JOIN t2 ON t1.id = t2.id;
+----+------------+----+------------+
| id | desc | id | desc |
+----+------------+----+------------+
| 2 | ID 2 in t1 | 2 | ID 2 in t2 |
| 3 | ID 3 in t1 | 3 | ID 3 in t2 |
+----+------------+----+------------+
mysql> SELECT *
FROM t1 INNER JOIN t2 ON t1.id = t2.id;
mysql> SELECT *
FROM t1 JOIN t2 ON t1.id = t2.id; -- default JOIN is INNER JOIN
mysql> SELECT *
FROM t1 CROSS JOIN t2 ON t1.id = t2.id; -- Also called CROSS JOIN
-- You can use USING clause if the join-columns have the same name
mysql> SELECT *
FROM t1 INNER JOIN t2 USING (id);
+----+------------+------------+
| id | desc | desc |
+----+------------+------------+
| 2 | ID 2 in t1 | ID 2 in t2 |
| 3 | ID 3 in t1 | ID 3 in t2 |
+----+------------+------------+
-- Only 3 columns in the result set, instead of 4 columns with ON clause
mysql> SELECT *
FROM t1 INNER JOIN t2 WHERE t1.id = t2.id; -- Use WHERE instead of ON
mysql> SELECT *
FROM t1, t2 WHERE t1.id = t2.id; -- Use "commas" operator to join
mysql> SELECT *
FROM t1 LEFT JOIN t2 USING (id);
+----+------------+------------+
| id | desc | desc |
+----+------------+------------+
| 1 | ID 1 in t1 | NULL |
| 2 | ID 2 in t1 | ID 2 in t2 |
| 3 | ID 3 in t1 | ID 3 in t2 |
+----+------------+------------+
mysql> SELECT *
FROM t1 RIGHT JOIN t2 ON t1.id = t2.id;
+------+------------+----+------------+
| id | desc | id | desc |
+------+------------+----+------------+
| 2 | ID 2 in t1 | 2 | ID 2 in t2 |
| 3 | ID 3 in t1 | 3 | ID 3 in t2 |
| NULL | NULL | 4 | ID 4 in t2 |
+------+------------+----+------------+
mysql> SELECT *
FROM t1 RIGHT JOIN t2 USING (id);
+----+------------+------------+
| id | desc | desc |
+----+------------+------------+
| 2 | ID 2 in t2 | ID 2 in t1 |
| 3 | ID 3 in t2 | ID 3 in t1 |
| 4 | ID 4 in t2 | NULL |
+----+------------+------------+
As the result, LEFT JOIN ensures that the result set contains every row on the left table. This is important,
as in some queries, you are interested to have result on every row on the left table, with no match in the
right table, e.g., searching for items without supplier. For example,
mysql> SELECT t1.id, t1.desc
FROM t1 LEFT JOIN t2 USING (id)
WHERE t2.id IS NULL;
+----+------------+
| id | desc |
+----+------------+
| 1 | ID 1 in t1 |
+----+------------+
mysql> SELECT *
FROM t1 LEFT JOIN t2 ON t1.id = t2.id;
mysql> SELECT *
FROM t1 LEFT OUTER JOIN t2 ON t1.id = t2.id;
mysql> SELECT *
FROM t1 LEFT JOIN t2 USING (id); -- join-columns have same name
+----+------------+------------+
| id | desc | desc |
+----+------------+------------+
| 1 | ID 1 in t1 | NULL |
| 2 | ID 2 in t1 | ID 2 in t2 |
| 3 | ID 3 in t1 | ID 3 in t2 |
+----+------------+------------+
7. Exercises
7.1 Rental System
Peter runs a small car rental company with 10 cars and 5 trucks. He engages you to design a web portal to
put his operation online.
For the initial phase, the web portal shall provide these basic functions:
1. Maintaining the records of the vehicles and customers.
2. Inquiring about the availability of vehicle, and
3. Reserving a vehicle for rental.
A vehicle, identified by the vehicle registration number, can be rented on a daily basis. The rental rate is
different for different vehicles. There is a discount of 20% for rental of 7 days or more.
A customer can rental a vehicle from a start date to an end date. A special customer discount, ranging from
0-50%, can be given to preferred customers.
Database
The initial database contains 3 tables: vehicles, customers, and rental_records.
The rental_records is a junction table supporting many-to-many relationship
between vehicles and customers.
10. SELECT
11. r.start_date AS `Start Date`,
12. r.end_date AS `End Date`,
13. r.veh_reg_no AS `Vehicle No`,
14. v.brand AS `Vehicle Brand`,
15. c.name AS `Customer Name`
16. FROM rental_records AS r
17. INNER JOIN vehicles AS v USING (veh_reg_no)
18. INNER JOIN customers AS c USING (customer_id)
19. List all the expired rental records (end_date before CURDATE()).
20. List the vehicles rented out on '2012-01-10' (not available for rental), in columns of vehicle
registration no, customer name, start date and end date. (Hint: the given date is in between
the start_date and end_date.)
21. List all vehicles rented out today, in columns registration number, customer name, start date, end
date.
22. Similarly, list the vehicles rented out (not available for rental) for the period from '2012-01-
03' to '2012-01-18'. (Hint: start_date is inside the range; or end_date is inside the range;
or start_date is before the range and end_date is beyond the range.)
23. List the vehicles (registration number, brand and description) available for rental (not rented out)
on '2012-01-10' (Hint: You could use a subquery based on a earlier query).
24. Similarly, list the vehicles available for rental for the period from '2012-01-03' to '2012-01-
18'.
25. Similarly, list the vehicles available for rental from today for 10 days.
26. Foreign Key Test:
a.Try deleting a parent row with matching row(s) in child table(s), e.g.,
delete 'GA6666F' from vehicles table (ON DELETE RESTRICT).
b. Try updating a parent row with matching row(s) in child table(s), e.g.,
rename 'GA6666F' to 'GA9999F' in vehicles table. Check the effects on the child
table rental_records (ON UPDATE CASCADE).
c. Remove 'GA6666F' from the database (Hints: Remove it from child
table rental_records; then parent table vehicles.)
27. Payments: A rental could be paid over a number of payments (e.g., deposit, installments, full
payment). Each payment is for one rental. Create a new table called payments. Need to create
columns to facilitate proper audit check (such
as create_date, create_by, last_update_date, last_update_by, etc.)
a image in MySQL Workbench ⇒ right-click on the cell ⇒ Load Value From File ⇒ Select the image
You can conveniently load and view the photo via graphical tools such as MySQL Workbench. To load
file. To view the image ⇒ right-click on the BLOB cell ⇒ Open Value in Editor ⇒ choose "Image" pane.
I also include a Java program for reading and writing image BLOB from/to the database, based on this
example: "TestImageBLOB.java".
4. VIEW: Create a VIEW called rental_prices on the rental_records with an additional column
called price. Show all the records of the VIEW.
price *= (1 - perferred_customer_discount);