Sie sind auf Seite 1von 61

To access the mysql command in Windows without manually changing changing

directories, do this:

Go to Control Panel > System > Advanced system settings.

System Properties will appear.
Click on the 'Advanced' tab.
Click 'Environment Variables'.
12 down Under System Variables, locate 'Path' and click Edit.
vote Append the path to your MySQL installation to the end of the exisiting
'Variable value'. Example:


or, if you prefer


1. Installation of MySQL 5.5.28 Server on RHEL/CentOS/Fedora

1. How to set MySQL Root password?

If you have fresh installation of MySQL server, then it doesnt required any password to connect
it as root user. To set MySQL password for root user, use the following command.

# mysqladmin -u root password YOURNEWPASSWORD

2. How to Change MySQL Root password?

If you would like to change or update MySQL root password, then you need to type the
following command. For example, say your old password is 123456 and you want to change it
with new password say xyz123.
mysqladmin -u root -p123456 password 'xyz123'

3. How to check MySQL Server is running?

To find out whether MySQL server is up and running, use the following command.

# mysqladmin -u root -p ping

Enter password:
mysqld is alive

4. How to Check which MySQL version I am running?

The following command shows MySQL version along with the current running status .

# mysqladmin -u root -p version

Enter password:
mysqladmin Ver 8.42 Distrib 5.5.28, for Linux on i686
Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

Server version 5.5.28

Protocol version 10
Connection Localhost via UNIX socket
UNIX socket /var/lib/mysql/mysql.sock
Uptime: 7 days 14 min 45 sec

Threads: 2 Questions: 36002 Slow queries: 0 Opens: 15 Flush tables: 1

Open tables: 8 Queries per second avg: 0.059

5. How to Find out current Status of MySQL server?

To find out current status of MySQL server, use the following command. The mysqladmin
command shows the status of uptime with running threads and queries.

# mysqladmin -u root -ptmppassword status

Enter password:
Uptime: 606704 Threads: 2 Questions: 36003 Slow queries: 0 Opens: 15
Flush tables: 1 Open tables: 8 Queries per second avg: 0.059

6. How to check status of all MySQL Server Variables and values?

To check all the running status of MySQL server variables and values, type the following
command. The output would be similar to below.

# mysqladmin -u root -p extended-status

Enter password:
| Variable_name | Value |
| Aborted_clients | 3 |
| Aborted_connects | 3 |
| Binlog_cache_disk_use | 0 |
| Binlog_cache_use | 0 |
| Binlog_stmt_cache_disk_use | 0 |
| Binlog_stmt_cache_use | 0 |
| Bytes_received | 6400357 |
| Bytes_sent | 2610105 |
| Com_admin_commands | 3 |
| Com_assign_to_keycache | 0 |
| Com_alter_db | 0 |
| Com_alter_db_upgrade | 0 |
| Com_alter_event | 0 |
| Com_alter_function | 0 |
| Com_alter_procedure | 0 |
| Com_alter_server | 0 |
| Com_alter_table | 0 |
| Com_alter_tablespace | 0 |

7. How to see all MySQL server Variables and Values?

To see all the running variables and values of MySQL server, use the command as follows.

# mysqladmin -u root -p variables

Enter password:
| Variable_name | Value
| auto_increment_increment | 1
| auto_increment_offset | 1
| autocommit | ON
| automatic_sp_privileges | ON
| back_log | 50
| basedir | /usr
| big_tables | OFF
| binlog_cache_size | 32768
| binlog_direct_non_transactional_updates | OFF
| binlog_format | STATEMENT
| binlog_stmt_cache_size | 32768
| bulk_insert_buffer_size | 8388608
| character_set_client | latin1
| character_set_connection | latin1
| character_set_database | latin1
| character_set_filesystem | binary
| character_set_results | latin1
| character_set_server | latin1
| character_set_system | utf8
| character_sets_dir |
/usr/share/mysql/charsets/ |
| collation_connection | latin1_swedish_ci

8. How to check all the running Process of MySQL server?

The following command will display all the running process of MySQL database queries.

# mysqladmin -u root -p processlist

Enter password:
| Id | User | Host | db | Command | Time | State | Info
| 18001 | rsyslog | localhost:38307 | rsyslog | Sleep | 5590 | |
| 18020 | root | localhost | | Query | 0 | | show
processlist |

9. How to create a Database in MySQL server?

To create a new database in MySQL server, use the command as shown below.

# mysqladmin -u root -p create databasename

Enter password:
# mysql -u root -p

Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 18027
Server version: 5.5.28 MySQL Community Server (GPL) by Remi

Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

Type 'help;' or '\h' for help. Type '\c' to clear the current input

mysql> show databases;

| Database |
| information_schema |
| databasename |
| mysql |
| test |
8 rows in set (0.01 sec)


10. How to drop a Database in MySQL server?

To drop a Database in MySQL server, use the following command. You will be asked to
confirm press y.

# mysqladmin -u root -p drop databasename

Enter password:
Dropping the database is potentially a very bad thing to do.
Any data stored in the database will be destroyed.

Do you really want to drop the 'databasename' database [y/N] y

Database "databasename" dropped

11. How to reload/refresh MySQL Privileges?

The reload command tells the server to reload the grant tables. The refresh command flushes all
tables and reopens the log files.

# mysqladmin -u root -p reload;

# mysqladmin -u root -p refresh
12. How to shutdown MySQL server Safely?

To shutdown MySQL server safely, type the following command.

mysqladmin -u root -p shutdown

Enter password:

You can also use the following commands to start/stop MySQL server.

# /etc/init.d/mysqld stop
# /etc/init.d/mysqld start

13. Some useful MySQL Flush commands

Following are some useful flush commands with their description.

1. flush-hosts: Flush all host information from host cache.

2. flush-tables: Flush all tables.
3. flush-threads: Flush all threads cache.
4. flush-logs: Flush all information logs.
5. flush-privileges: Reload the grant tables (same as reload).
6. flush-status: Clear status variables.

# mysqladmin -u root -p flush-hosts

# mysqladmin -u root -p flush-tables
# mysqladmin -u root -p flush-threads
# mysqladmin -u root -p flush-logs
# mysqladmin -u root -p flush-privileges
# mysqladmin -u root -p flush-status

14. How to kill Sleeping MySQL Client Process?

Use the following command to identify sleeping MySQL client process.

# mysqladmin -u root -p processlist

Enter password:
| Id | User | Host | db | Command | Time | State | Info |
| 5 | root | localhost | | Sleep | 14 | |
| 8 | root | localhost | | Query | 0 | | show processlist |

Now, run the following command with kill and process ID as shown below.

# mysqladmin -u root -p kill 5

Enter password:
| Id | User | Host | db | Command | Time | State | Info |
| 12 | root | localhost | | Query | 0 | | show processlist |

If you like to kill multiple process, then pass the process IDs with comma separated as shown

# mysqladmin -u root -p kill 5,10

15. How to run multiple mysqladmin commands together?

If you would like to execute multiple mysqladmin commands together, then the command
would be like this.

# mysqladmin -u root -p processlist status version

Enter password:
| Id | User | Host | db | Command | Time | State | Info |
| 8 | root | localhost | | Query | 0 | | show processlist |
Uptime: 3801 Threads: 1 Questions: 15 Slow queries: 0 Opens: 15 Flush
tables: 1 Open tables: 8 Queries per second avg: 0.003
mysqladmin Ver 8.42 Distrib 5.5.28, for Linux on i686
Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

Server version 5.5.28

Protocol version 10
Connection Localhost via UNIX socket
UNIX socket /var/lib/mysql/mysql.sock
Uptime: 1 hour 3 min 21 sec

Threads: 1 Questions: 15 Slow queries: 0 Opens: 15 Flush tables: 1 Open

tables: 8 Queries per second avg: 0.003

16. How to Connect remote mysql server

To connect remote MySQL server, use the -h (host) with IP Address of remote machine.

# mysqladmin -h -u root -p

17. How to execute command on remote MySQL server

Lets say you would like to see the status of remote MySQL server, then the command would
# mysqladmin -h -u root -p status

18. How to start/stop MySQL replication on a slave server?

To start/stop MySQL replication on salve server, use the following commands.

# mysqladmin -u root -p start-slave

# mysqladmin -u root -p stop-slave

19. How to store MySQL server Debug Information to logs?

It tells the server to write debug information about locks in use, used memory and query usage to
the MySQL log file including information about event scheduler.

# mysqladmin -u root -p debug

Enter password:

20. How to view mysqladmin options and usage

To find out more options and usage of myslqadmin command use the help command as shown
below. It will display a list of available options.

# mysqladmin --help

We have tried our best to include almost all of mysqladmin commands with their examples in
this article, If still, weve missed anything, please do let us know via comments and dont forget
to share with your friends.


MySQL Basic Database Administration Commands Part I

by Avishek Kumar | Published: September 6, 2013 | Last Updated: September 6, 2013

Download Your Free eBooks NOW - 10 Free Linux eBooks for Administrators | 4 Free Shell Scripting

Database is a structured set of data stored electronically. The concept of database was known to
our ancestors even when there were no computers, however creating and maintaining such
database was very tedious job. In a manual database say of 100 pages, if you have to search for
all the employees whose salary were less than 10k, just think how much difficult it would have
been, then.
In todays world you just cant escape Database. Right now millions of database is working
around the world to store and fetch data of every kind be it strategic data, employee record or
web technologies.

MySQL Administration Guide Part I

Database is oftenly termed as back-end process, as because it is neither visible to end user nor
End User interacts directly with the database. They works on front-end process viz., PHP, VB,
ASP.NET, etc. and ask the front end to deal with database in back-end.

There are several database server and client available like Oracle, MySQL, MySQLi, MariaDB,
MongoDB etc. The syntax of all of these are more or less the same. Mastering one means
gaining control on most of them and learning the queries of a database is very easy and fun.

Lets start with simple queries on database. We will be using MySQL which comes bundled with
most of the Linux distributions by default, you could install it manually from repository, if it is
not installed by default in your case.

Well a database query is a simple piece of code that is sent to database to get custom and refined
result, as required.

Install MySQL Database

Use yum or apt package manager to install MySQL Database.

# yum install mysql mysql-client mysql-server (on Yum based Systems)

# apt-get install mysql mysql-client mysql-server (on Apt based Systems)

Start MySQL

Start MySQL database service as:

# service mysqld start

# service mysql start

Well installing a MySQL database will take you to the configuration where you are asked to
setup admin password, etc. Once finished installing and starting the server go to your MySQL

# mysql -u root -p

Replace root with your configured username and enter password when prompted, if the login
credential is correct, you will be at your MySQL prompt at the blink of your eyes.

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 195

Server version: 5.5.31-0+wheezy1 (Debian)

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its affiliates.

Other names may be trademarks of their respective owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input


Now carrying out queries at this prompt is very educative and fun.

Create a database tecmint

mysql> create database tecmint ;
Query OK, 1 row affected (0.02 sec)


Note: It reports that the query was correct, means database is created. You can verify your newly
created database as.

mysql> show databases;

| Database |
| information_schema |
| mysql |
| performance_schema |
| tecmint |
| test |
9 rows in set (0.00 sec)

Note: Notice your database in the above output.

Select Database

Now you need to select the database to work upon it.

mysql> use tecmint;

Database changed

Create Tables in MySQL

Here we will be creating a table say minttec with three fields as:

mysql> CREATE TABLE minttec (

-> id Int(3),
-> first_name Varchar (15),
-> email Varchar(20)
-> );
Query OK, 0 rows affected (0.08 sec)

Note: The above query says OK which means table was created without any error. To verify the
table run the below query.

mysql> show tables;

| Tables_in_tecmint |
| minttec |

1 row in set (0.00 sec)


Things are going fine till now. Yup! You can view the columns you created in the table
minttec as:

mysql> show columns from minttec;

| Field | Type | Null | Key | Default | Extra |
| id | int(3) | YES | | NULL | |
| first_name | varchar(15) | YES | | NULL | |
| email | varchar(20) | YES | | NULL | |
3 rows in set (0.00 sec)


It was nothing less than a magic. Anyway I will tell you about the types of declaration and their

1. Int is Integer
2. Varchar is char having variable length as defined. The value after Type is the length of field up-to
which it can store data.

OK now we need to add a column say last_name after column first_name.

mysql> ALTER TABLE minttec ADD last_name varchar (20) AFTER first_name;
Query OK, 0 rows affected (0.16 sec)
Records: 0 Duplicates: 0 Warnings: 0

Now, verify it in your table.

mysql> show columns from minttec;

| Field | Type | Null | Key | Default | Extra |
| id | int(3) | YES | | NULL | |
| first_name | varchar(15) | YES | | NULL | |
| last_name | varchar(20) | YES | | NULL | |
| email | varchar(20) | YES | | NULL | |

4 rows in set (0.00 sec)


Add Column in MySQL

Now we will add a column to the right say a column country to the right of email.

mysql> ALTER TABLE minttec ADD country varchar (15) AFTER email;
Query OK, 0 rows affected (0.16 sec)
Records: 0 Duplicates: 0 Warnings: 0


Verify the above column insertion query.

mysql> show columns from minttec;

| Field | Type | Null | Key | Default | Extra |
| id | int(3) | YES | | NULL | |
| first_name | varchar(15) | YES | | NULL | |
| last_name | varchar(20) | YES | | NULL | |
| email | varchar(20) | YES | | NULL | |
| country | varchar(15) | YES | | NULL | |
5 rows in set (0.00 sec)


Insert Values in Field

What about inserting values to the field?

mysql> INSERT INTO minttec VALUES ('1' , 'Ravi' , 'Saive' ,

'' , 'India' );
Query OK, 1 row affected (0.02 sec)


How about inserting more than 1 value at a time in the above table.

mysql> INSERT INTO minttec VALUES ('2' , 'Narad' , 'Shrestha' ,

'' , 'India' ), ('3' , 'user' , 'singh' , '' , 'Aus'
), ('4' , 'tecmint' , '[dot]com' , '' , 'India' );
Query OK, 3 rows affected (0.05 sec)
Records: 3 Duplicates: 0 Warnings: 0

Verify the above insertion.

mysql> select * from minttec;

| id | first_name | last_name | email | country |
| 1 | Ravi | Saive | | India |
| 2 | Narad | Shrestha | | India |
| 3 | user | singh | | Aus |
| 4 | tecmint | [dot]com | | India |

4 rows in set (0.00 sec)


Delete Values in Field

Lets say the third entry in the above output is invalid and we need to delete the third entry.

mysql> DELETE FROM minttec WHERE id = 3;

Query OK, 1 row affected (0.02 sec)

Verify the above operation.

mysql> select * from minttec;

| id | first_name | last_name | email | country |
| 1 | Ravi | Saive | | India |
| 2 | Narad | Shrestha | | India |
| 4 | tecmint | [dot]com | | India |
3 rows in set (0.00 sec)

Update Values in Field

The id (=4) needs to be edited.

mysql> UPDATE minttec SET id = 3 WHERE first_name = 'tecmint';

Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0


Verify the above query.

mysql> UPDATE minttec SET id = 3 WHERE first_name = 'tecmint';

Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0


Note: The above query, as performed is not a good idea. It will change the id to 4 where ever
the first name is tecmint. It is always a good idea to use more than one column with where
clause to get minimal error, as:

mysql> UPDATE minttec SET id = 6 WHERE first_name = 'tecmint'AND last_name =

Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0


Delete Column in MySQL

Let we need to drop (delete) a column we think, is of no importance say country here.

mysql> ALTER TABLE minttec drop country;

Query OK, 3 rows affected (0.15 sec)
Records: 3 Duplicates: 0 Warnings: 0


Verify the table.

mysql> select * from minttec;

| id | first_name | last_name | email |
| 1 | Ravi | Saive | |
| 2 | Narad | Shrestha | |
| 6 | tecmint | [dot]com | |
3 rows in set (0.00 sec)


Rename Table in MySQL

Dont you think our table name minttec is not very much relevant. How about changing it to

mysql> RENAME TABLE minttec TO tecmint_table;

Query OK, 0 rows affected (0.03 sec)


List all Tables

See all the tables under the current database.

mysql> show tables;

| Tables_in_tecmint |
| tecmint_table |
1 row in set (0.00 sec)


The table has been renamed. Now take a backup of the above MySQL database, in a single line
of command without any sophisticated tool. Run the below code at your terminal and not on
mysql prompt.

# mysqldump -u root -p tecmint > tecmint.sql

check the dumped file on your desktop which would have contents something
-- MySQL dump 10.13 Distrib 5.5.31, for debian-linux-gnu (i686) --
-- Server version 5.5.31-0+wheezy1 --
Dump completed on 2013-09-02 12:55:37

It is always a good idea to maintain Backup of MySQL databases. Restoring the backed up
MySQL Data is again a simple line of code you need to run at your terminal prompt and not at
your mysql prompt.
But, wait first we will delete the database to verify if our restore is perfect.

Delete a Database
mysql> drop database tecmint;
Query OK, 1 row affected (0.02 sec)

Check for database tecmint on your database server.

mysql> show databases;

| Database |
| information_schema |
| my_database |
| mysql |
| performance_schema |
| phpmyadmin |
| sisso |
| test |

7 rows in set (0.00 sec)


Great! The database is lost, but we need not to worry, we are having the backup.

Restore a Database

To restore lost database, run the following command.

# mysql -u root -p tecmint < tecmint.sql

Enter password:
ERROR 1049 (42000): Unknown database 'tecmint'

OOPS! An error, hey we have not create the database tecmint. So go to your mysql prompt and
create a database tecmint.

mysql> create database tecmint;

Query OK, 1 row affected (0.00 sec)


Now time to run restore command at your shell prompt (strictly).

# mysql -u root -p tecmint < tecmint.sql

Enter password:

Verify your database.

mysql> show databases;

| Database |
| information_schema |
| mysql |
| performance_schema |
| tecmint |
| test |
8 rows in set (0.00 sec)

Verify the contents of database.

mysql> show tables from tecmint;

| Tables_in_tecmint |
| tecmint_table |
1 row in set (0.00 sec)


Verify the contents of your restored table.

mysql> select * from tecmint_table;

| id | first_name | last_name | email |
| 1 | Ravi | Saive | |
| 2 | Narad | Shrestha | |
| 6 | tecmint | [dot]com | |

3 rows in set (0.00 sec)

This is not the end definitely, we will cover the concept of primary key, foreign key, multiple
tables and running queries using simple PHP script in the next part of the article.

Dont forget to tell us, how you felt while going through the article. Your comments are highly
appreciated. Stay Healthy and Tuned, remain connected to Tecmint.

MySQL "show users" - how to show/list the users in a MySQL database

By Alvin Alexander. Last updated: May 7, 2015

MySQL users FAQ: How do I show/list MySQL users, i.e., the user accounts in a MySQL
To show/list the users in a MySQL database, first log into your MySQL server as an
administrative user using the mysql client, then run this MySQL query:

mysql> select * from mysql.user;

However, note that this query shows a large listing of MySQL user information, including user
permission information, so as a practical matter you may want to trim down some of the fields to
display, something like this:

mysql> select host, user, password from mysql.user;

The next section provides details and background information about this second query.

How to reduce the amount of user information shown

You can get a listing of the fields in the mysql.user table by running this MySQL query:

mysql> desc mysql.user;

On my current server this shows the following 37 columns of MySQL user information, as
shown here:

mysql> desc mysql.user;

| Field | Type | Null | Key |
Default | Extra |
| Host | char(60) | NO | PRI |
| |
| User | char(16) | NO | PRI |
| |
| Password | char(41) | NO | |
| |
| Select_priv | enum('N','Y') | NO | | N
| |
| Insert_priv | enum('N','Y') | NO | | N
| |
| Update_priv | enum('N','Y') | NO | | N
| |
| Delete_priv | enum('N','Y') | NO | | N
| |
| Create_priv | enum('N','Y') | NO | | N
| |
| Drop_priv | enum('N','Y') | NO | | N
| |
| Reload_priv | enum('N','Y') | NO | | N
| |
| Shutdown_priv | enum('N','Y') | NO | | N
| |
| Process_priv | enum('N','Y') | NO | | N
| |
| File_priv | enum('N','Y') | NO | | N
| |
| Grant_priv | enum('N','Y') | NO | | N
| |
| References_priv | enum('N','Y') | NO | | N
| |
| Index_priv | enum('N','Y') | NO | | N
| |
| Alter_priv | enum('N','Y') | NO | | N
| |
| Show_db_priv | enum('N','Y') | NO | | N
| |
| Super_priv | enum('N','Y') | NO | | N
| |
| Create_tmp_table_priv | enum('N','Y') | NO | | N
| |
| Lock_tables_priv | enum('N','Y') | NO | | N
| |
| Execute_priv | enum('N','Y') | NO | | N
| |
| Repl_slave_priv | enum('N','Y') | NO | | N
| |
| Repl_client_priv | enum('N','Y') | NO | | N
| |
| Create_view_priv | enum('N','Y') | NO | | N
| |
| Show_view_priv | enum('N','Y') | NO | | N
| |
| Create_routine_priv | enum('N','Y') | NO | | N
| |
| Alter_routine_priv | enum('N','Y') | NO | | N
| |
| Create_user_priv | enum('N','Y') | NO | | N
| |
| ssl_type | enum('','ANY','X509','SPECIFIED') | NO | |
| |
| ssl_cipher | blob | NO | |
NULL | |
| x509_issuer | blob | NO | |
NULL | |
| x509_subject | blob | NO | |
NULL | |
| max_questions | int(11) unsigned | NO | | 0
| |
| max_updates | int(11) unsigned | NO | | 0
| |
| max_connections | int(11) unsigned | NO | | 0
| |
| max_user_connections | int(11) unsigned | NO | | 0
| |
37 rows in set (0.10 sec)
So for most cases where you want to show MySQL user accounts you'll probably want to limit
your MySQL users' query to a few important columns, something like this:

select host, user, password from mysql.user;

In summary, if you need to list the users in a MySQL database, I hope this has been helpful.

About MySQL

MySQL is an open source database management software that helps users store, organize, and
later retrieve data. It has a variety of options to grant specific users nuanced permissions within
the tables and databasesthis tutorial will give a short overview of a few of the many options.

How to Create a New User

In Part 1 of the MySQL Tutorial, we did all of the editing in MySQL as the root user, with full
access to all of the databases. However, in the cases where more restrictions may be required,
there are ways to create users with custom permissions.

Lets start by making a new user within the MySQL shell:

CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';

Sadly, at this point newuser has no permissions to do anything with the databases. In fact, if
newuser even tries to login (with the password, password), they will not be able to reach the
MySQL shell.

Therefore, the first thing to do is to provide the user with access to the information they will

GRANT ALL PRIVILEGES ON * . * TO 'newuser'@'localhost';

The asterisks in this command refer to the database and table (respectively) that they can
accessthis specific command allows to the user to read, edit, execute and perform all tasks
across all the databases and tables.

Once you have finalized the permissions that you want to set up for your new users, always be
sure to reload all the privileges.


Your changes will now be in effect.

How To Grant Different User Permissions

Here is a short list of other common possible permissions that users can enjoy.
ALL PRIVILEGES- as we saw previously, this would allow a MySQL user all access to a designated
database (or if no database is selected, across the system)
CREATE- allows them to create new tables or databases
DROP- allows them to them to delete tables or databases
DELETE- allows them to delete rows from tables
INSERT- allows them to insert rows into tables
SELECT- allows them to use the Select command to read through databases
UPDATE- allow them to update table rows
GRANT OPTION- allows them to grant or remove other users' privileges

To provide a specific user with a permission, you can use this framework:

GRANT [type of permission] ON [database name].[table name] TO


If you want to give them access to any database or to any table, make sure to put an asterisk (*)
in the place of the database name or table name.

Each time you update or change a permission be sure to use the Flush Privileges command.

If you need to revoke a permission, the structure is almost identical to granting it:

REVOKE [type of permission] ON [database name].[table name] FROM


Just as you can delete databases with DROP, you can use DROP to delete a user altogether:

DROP USER demo@localhost;

To test out your new user, log out by typing


and log back in with this command in terminal:

mysql -u [username]-p

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 customer record contains his/her name, address and phone number.

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

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.


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.


CREATE DATABASE `rental_db`;
USE `rental_db`;

-- Create `vehicles` table

CREATE TABLE `vehicles` (
`veh_reg_no` VARCHAR(8) NOT NULL,
`category` ENUM('car', 'truck') NOT NULL DEFAULT 'car',
-- Enumeration of one of the items in the list
-- desc is a keyword (for descending) and must be back-
`photo` BLOB NULL, -- binary large object of up to 64KB
-- to be implemented later
`daily_rate` DECIMAL(6,2) NOT NULL DEFAULT 9999.99,
-- set default to max value
PRIMARY KEY (`veh_reg_no`),
INDEX (`category`) -- Build index on this column for fast search
-- MySQL provides a few ENGINEs.
-- The InnoDB Engine supports foreign keys and transactions
DESC `vehicles`;
SHOW INDEX FROM `vehicles` \G

-- Create `customers` table

CREATE TABLE `customers` (
-- Always use INT for AUTO_INCREMENT column to avoid run-
`address` VARCHAR(80) NOT NULL DEFAULT '',
PRIMARY KEY (`customer_id`),
UNIQUE INDEX (`phone`), -- Build index on this unique-value column
INDEX (`name`) -- Build index on this column
DESC `customers`;
SHOW CREATE TABLE `customers` \G
SHOW INDEX FROM `customers` \G

-- Create `rental_records` table

DROP TABLE IF EXISTS `rental_records`;
CREATE TABLE `rental_records` (
`veh_reg_no` VARCHAR(8) NOT NULL,
`customer_id` INT UNSIGNED NOT NULL,
`start_date` DATE NOT NULL DEFAULT '0000-00-00',
`end_date` DATE NOT NULL DEFAULT '0000-00-00',
-- Keep the created and last updated timestamp for auditing and
PRIMARY KEY (`rental_id`),
FOREIGN KEY (`customer_id`) REFERENCES `customers` (`customer_id`)
-- Disallow deletion of parent record if there are matching records
-- If parent record (customer_id) changes, update the matching records
FOREIGN KEY (`veh_reg_no`) REFERENCES `vehicles` (`veh_reg_no`)
DESC `rental_records`;
SHOW CREATE TABLE `rental_records` \G
SHOW INDEX FROM `rental_records` \G

-- Inserting test records

('SBA1111A', 'car', 'NISSAN SUNNY 1.6L', '4 Door Saloon, Automatic', NULL,
('SBB2222B', 'car', 'TOYOTA ALTIS 1.6L', '4 Door Saloon, Automatic', NULL,
('SBC3333C', 'car', 'HONDA CIVIC 1.8L', '4 Door Saloon, Automatic', NULL,
('GA5555E', 'truck', 'NISSAN CABSTAR 3.0L', 'Lorry, Manual ', NULL,
('GA6666F', 'truck', 'OPEL COMBO 1.6L', 'Van, Manual', NULL, 69.99);
-- No photo yet, set to NULL
SELECT * FROM `vehicles`;


(1001, 'Tan Ah Teck', '8 Happy Ave', '88888888', 0.1),
(NULL, 'Mohammed Ali', '1 Kg Java', '99999999', 0.15),
(NULL, 'Kumar', '5 Serangoon Road', '55555555', 0),
(NULL, 'Kevin Jones', '2 Sunset boulevard', '22222222', 0.2);
SELECT * FROM `customers`;
INSERT INTO `rental_records` VALUES
(NULL, 'SBA1111A', 1001, '2012-01-01', '2012-01-21', NULL),
(NULL, 'SBA1111A', 1001, '2012-02-01', '2012-02-05', NULL),
(NULL, 'GA5555E', 1003, '2012-01-05', '2012-01-31', NULL),
(NULL, 'GA6666F', 1004, '2012-01-20', '2012-02-20', NULL);
SELECT * FROM `rental_records`;


1. Customer 'Tan Ah Teck' has rented 'SBA1111A' from today for 10 days. (Hint: You
need to insert a rental record. Use a SELECT subquery to get the customer_id. Use
CURDATE() (or NOW()) for today; and DATE_ADD(CURDATE(), INTERVAL x unit) to
compute a future date.)
2. INSERT INTO rental_records VALUES
3. (NULL,
4. 'SBA1111A',
5. (SELECT customer_id FROM customers WHERE name='Tan Ah Teck'),

8. Customer 'Kumar' has rented 'GA5555E' from tomorrow for 3 months.

9. List all rental records (start date, end date) with vehicle's registration number, brand, and
customer name, sorted by vehicle's categories followed by start date.
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. 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)
ORDER BY v.category, start_date;

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
25. Similarly, list the vehicles available for rental from today for 10 days.
26. Foreign Key Test:
1. Try deleting a parent row with matching row(s) in child table(s), e.g., delete
'GA6666F' from vehicles table (ON DELETE RESTRICT).
2. 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).
3. 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.)
28. DROP TABLE IF EXISTS `payments`;
29. CREATE TABLE payments (
31. `rental_id` INT UNSIGNED NOT NULL,
32. `amount` DECIMAL(8,2) NOT NULL DEFAULT 0,
33. `mode` ENUM('cash', 'credit card', 'check'),
34. `type` ENUM('deposit', 'partial', 'full') NOT NULL DEFAULT
35. `remark` VARCHAR(255),
36. `created_date` DATETIME NOT NULL,
37. `created_by` INT UNSIGNED NOT NULL, -- staff_id
38. -- Use a trigger to update create_date and
create_by automatically
40. -- Updated by the system automatically
41. `last_updated_by` INT UNSIGNED NOT NULL,
42. -- Use a trigger to update created_by
43. PRIMARY KEY (`payment_id`),
44. INDEX (`rental_id`),
45. FOREIGN KEY (`rental_id`) REFERENCES rental_records (`rental_id`)
46. ) ENGINE=InnoDB;
47. DESC `payments`;
48. SHOW CREATE TABLE `payments` \G
SHOW INDEX FROM `payments` \G

49. Staff: Keeping track of staff serving the customers. Create a new staff table. Assume
that each transaction is handled by one staff, we can add a new column called staff_id
in the rental_records table,
51. CREATE TABLE `staff` (
53. -- Always use INT for AUTO_INCREMENT column to prvent
54. `name` VARCHAR(30) NOT NULL DEFAULT '',
55. `title` VARCHAR(30) NOT NULL DEFAULT '',
56. `address` VARCHAR(80) NOT NULL DEFAULT '',
57. `phone` VARCHAR(15) NOT NULL DEFAULT '',
58. `report_to` INT UNSIGNED NOT NULL,
59. -- Reports to manager staff_id. Boss reports to
60. PRIMARY KEY (`staff_id`),
61. UNIQUE INDEX (`phone`), -- Build index on this unique-value column
62. INDEX (`name`), -- Build index on this column
63. FOREIGN KEY (`report_to`) REFERENCES `staff` (`staff_id`)
64. -- Reference itself
65. ) ENGINE=InnoDB;
66. DESC `staff`;
67. SHOW INDEX FROM `staff` \G
69. INSERT INTO staff VALUE (8001, 'Peter Johns', 'Managing Director', '1
Happy Ave', '12345678', 8001);
70. SELECT * FROM staff;
72. -- Add a new column to rental_records table
73. ALTER TABLE `rental_records` ADD COLUMN `staff_id` INT UNSIGNED NOT
74. -- Need to set to a valid value, before adding the foreign key
75. UPDATE `rental_records` SET `staff_id` = 8001;
76. ALTER TABLE `rental_records` ADD FOREIGN KEY (`staff_id`) REFERENCES
staff (`staff_id`)
79. SHOW CREATE TABLE `rental_records` \G
80. SHOW INDEX FROM `rental_records` \G
82. -- Also Add a new column to payments table
84. -- Need to set to a valid value, before adding the foreign key
85. UPDATE `payments` SET `staff_id` = 8001;
86. ALTER TABLE `payments` ADD FOREIGN KEY (`staff_id`) REFERENCES staff
89. SHOW CREATE TABLE `payments` \G
SHOW INDEX FROM `payments` \G

Advanced Exercises

1. Adding Photo: We could store photo in MySQL using data type of BLOB (Binary Large
Object) (up to 64KB), MEDIUMBLOB (up to 16MBytes), LONGBOLB (up to 4GBytes). For
2. -- Use function LOAD_FILE to load a picture file into a BLOB field
3. UPDATE vehicles SET photo=LOAD_FILE('d:/temp/car.jpg') WHERE veh_reg_no
= 'SBA1111A';
SELECT * FROM vehicles WHERE veh_reg_no = 'SBA1111A' \G

You can conveniently load and view the photo via graphical tools such as MySQL
Workbench. To load a image in MySQL Workbench right-click on the cell Load
Value From File Select the image 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: "".
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.
5. DROP VIEW IF EXISTS rental_prices;
6. CREATE VIEW rental_prices
7. AS
9. v.veh_reg_no AS `Vehicle No`,
10. v.daily_rate AS `Daily Rate`,
11. AS `Customer Name`,
12.*100 AS `Customer Discount (%)`,
13. r.start_date AS `Start Date`,
14. r.end_date AS `End Date`,
15. DATEDIFF(r.end_date, r.start_date) AS `Duration`,
16. -- Compute the rental price
17. -- Preferred customer has discount, 20% discount for 7 or more days
18. -- CAST the result from DOUBLE to DECIMAL(8,2)
19. CAST(
20. IF (DATEDIFF(r.end_date, r.start_date) < 7,
21. DATEDIFF(r.end_date, r.start_date)*daily_rate*(1-discount),
22. DATEDIFF(r.end_date, r.start_date)*daily_rate*(1-
23. AS DECIMAL(8,2)) AS price
24. FROM rental_records AS r
25. INNER JOIN vehicles AS v USING (veh_reg_no)
26. INNER JOIN customers AS c USING (customer_id);
28. DESC `rental_prices`;
29. SHOW CREATE VIEW `rental_prices` \G
31. -- Try selecting all rows
SELECT * FROM `rental_prices`;

It is probably easier to compute the price using a program/procedure, instead of inside the

32. From the payments table, create a view to show the outstanding balance.
33. Define more views.
34. FUNCTION: Write a function to compute the rental price.
35. Define more procedures and functions.
36. TRIGGER: Write a trigger for the created_date and created_by columns of the
payments table.
37. Define more triggers.
38. Implement discount on weekday (Monday to Friday, except public holiday): Need to set
up a new table called public_hoilday with columns date and description. Use
function DAYOFWEEK (1=Sunday, , 7=Saturday) to check for weekday or weekend.
39. -- pseudocode for calculating rental price
40. price = 0;
41. for each date from start_date to end_date {
42. if date is weekend or public_holiday, price += daily_rate;
43. else price += daily_rate*(1-discount);
44. }
45. if (duration >= 7) price *= (1 - long_duration_discount);
price *= (1 - perferred_customer_discount);
7.2 Product Sales Database

Managing Many to Many Relationships in MySQL Part 1

jaypipesDecember 8, 2005 18 Comments
Flexible, Scalable Key Mapping Solutions

In working to answer questions on the MySQL forums, Ive noticed

a few questions that repeatedly come up on a number of the forum areas. One of these particular
questions deals with how to manage construct, query, and maintain many to many
relationships in your schema. I decided to put together a two-part article series detailing some of
the common dilemmas which inevitably arise when tackling the issue of relating two entities
where one entity can be related to many instances of another, and vice versa.

Hopefully, this article will shed some light on how to structure your schema effectively to
produce fast, efficient queries, and also will illustrate how key map tables can be queried for a
variety of different purposes. Ill predominantly be using standard SQL, so although Im using
MySQL as the database of choice here, the code you see is for the most part not limited to
running on just MySQL. In this first part, well review the concepts involved in many-to-many
relationships, and discuss the most common methods for use in storing the data composing the
relationship. In the second part of the article, which I should complete in about another week,
well look at some solid performance numbers regarding the various approaches. Also, Ill show
you how to use MySQL 5 stored procedures and views in order to most effectively manage
many-to-many relationships.

A Review of Relational Concepts

For those of you unaware of what a many-to-many relationship is, lets first briefly discuss some
definitions Ill be using in the article. First, an entity, in the database world, is simply a singular
object or concept. An entity, just like a real world object, may have one or more attributes which
describe different aspects of the entity. A table in a properly normalized database contains
records which pertain to a single entity. These records represent instances of the entity.

To illustrate, lets assume were building a website that specializes in used-car sales. We need to
design a schema which will handle the searching and storage of a variety of different auto makes
and models, and allow customers to filter results based on a set of features they require in the

figure A Auto Entity

The primary entity in our schema could be considered the Auto entity. It might have some
attributes such as a manufacturer, model, model year, and so forth. Figure A shows a depiction of
this simple Auto entity, with the primary key attribute (auto_id) in bold and above all other
descriptive attributes like manufacturer, model, etc.

One to Many Relationships

Entities in the database can relate to each other in a number of ways. The most common type of
relationship is called a one-to-many relationship. Here, one instance of an entity can relate, or be
attached to, many instances of another entity. In our sample schema, the Auto entity can have
only one auto manufacturer. However, an auto manufacturer can produce many automobiles.
Therefore, we say that the relationship from Auto Manufacturer to Auto is a one-to-many
relationship. Figure B depicts a one-to-many relationship between the Auto Manufacturer entity
and the Auto entity. In the figure, the line between the two entities represents the relationship.
This is a common way to represent relationships, with the one side of the relationship having a
single line and the many side of the relationship having a set of three lines and a circle.

figure B One to Many Relationship

Relationships are implemented via common attributes in each Entity called key attributes. In a
one-to-many relationship, these key attributes take the form of a parent key and a child, or
foreign, key. The relationship is maintained by connecting the two attributes together via these
key attributes. In the case of our Auto Manufacturer to Auto relationship, the key attributes are
AutoManufacturer.auto_manufacturer_id and Auto.auto_manufacturer_id. If we wanted to
list the Autos manufacturer name (not Manufacturer ID), we would use an INNER JOIN from
the Auto table to the AutoManufacturer table along this key relationship, as shown below.

FROM Auto a
INNER JOIN AutoManufacturer am
ON a.auto_manufacturer_id = am.auto_manufacturer_id
WHERE a.auto_id = 12345;

This simple example shows how a one-to-many relationship is implemented using a simple
INNER JOIN to find the intersection of two tables where the key attributes
AutoManufacturer.auto_manufacturer_id and Auto.auto_manufacturer_id contain
matching entries.

Many to Many Relationships

A many to many relationship is realized between two entities when either entity may be
associated with more than one instance of the other entity. For example, imagine the relationship
between an Auto (as in car) entity and an AutoFeature entity representing any of the myriad
options a car may come with.

figure C Many to Many Relationship

In this case, we know that any particular automobile can have many features. Likewise we know
that a specific automobile feature, say power windows, may be in any number of automobiles.
Theres no way for us to visually represent the association between the two entities without using
a third entity, which stores the mapping of the relationship between the Auto entity and the
AutoFeature entity. In this case, I use the Auto2AutoFeature entity. For mapping tables, I tend to
use this Something2Something naming scheme to clearly show that it is a table which primarily
serves to map the relationship from one thing to another, but of course that is merely a stylistic
convention, nothing more.

Schema Representations for Many to Many Relationships

There are a few common ways for representing many-to-many relationships within structured
SQL data, all of which will be detailed below:

1. Using multiple fields of an on/off data type to store many keys in a single table
2. Using the INT, BIGINT, or SET data type (or equivalent in other RDBMS) to store a fixed number
of key flags in a single table field
3. Using a CHAR string having one byte of storage per key needed, with the string acting as one
long imploded array in a single table field
4. Using a relationship, or mapping, table, like the one in figure C, to store one or more keys
related to an entity

All of these methods has distinct advantages and disadvantages, in both ease of use and
performance. Well look at each here, along with some sample code to show how common
queries are performed across each storage schema.

The Multiple Field Method

In this method of defining a many-to-many relationship, the concepts of normalization are

thrown away in favor of what some consider to be a simpler and more rational schema. Multiple
fields, each representing an on/off value for a foreign key, are used in only a single table in order
to achieve the results desired. Any data type representing an on/off value may be used to
represent the key fields CHAR(1) with T or F, Y or N, or a TINYINT UNSIGNED
with 0 and 1 values, or an ENUM(Y,N) etc. Below, youll see what a sample Auto table using
this method might look like with CHAR(1) data types used for the auto option key fields:


, auto_manufacturer_id SMALLINT UNSIGNED NOT NULL
, auto_model VARCHAR(20) NOT NULL
, asking_price DECIMAL(12,2) UNSIGNED NOT NULL
, has_air_conditioning CHAR(1) NOT NULL DEFAULT 'N'
, has_power_windows CHAR(1) NOT NULL DEFAULT 'N'
, has_power_steering CHAR(1) NOT NULL DEFAULT 'N'
, has_moonroof CHAR(1) NOT NULL DEFAULT 'N'
, has_disk_brakes CHAR(1) NOT NULL DEFAULT 'N'
, has_power_seats CHAR(1) NOT NULL DEFAULT 'N'
, has_leather CHAR(1) NOT NULL DEFAULT 'N'
, PRIMARY KEY pk_Auto (auto_id)

There are a couple advantages to this type of approach:

1. Given a simple SELECT * FROM the table, it is fairly easy to understand (immediately) what
options the car has.
2. Only one table is involved in determining options for the car.

This last point is often the reason why many novice developers choose to use this approach; its
easier and more straightforward to filter a resultset based on a single option:

FROM Auto a
WHERE a.has_leather = 'Y';

or more complicated request:

FROM Auto a
WHERE (a.has_leather = 'Y' OR a.has_power_windows = 'Y')
AND a.has_power_steering = 'Y';

The code is easy to understand for most anyone looking at it, and theres no special syntax or
SQL tricks needed in order to query on an OR condition or multiple various filters.
Unfortunately, this approach has a number of downsides. Most important among them are:

1. If you need to add an auto feature, youve got to ALTER TABLE Auto ADD COLUMN

This disadvantage is often overlooked in early design phases by novice programmers or

design teams because everyone always assumes that they know every option that the
customer might want to use. However, this is rarely the reality. Customers change their
minds and will inevitably ask to add or remove Auto Features from the list. With this
method, that requires a fundamental change in the schema: removing or adding columns.
When doing so, especially on large tables, deadlocks can easily occur while read requests
wait for an exclusive write lock to finish while the table is rebuilt to the new schema.
(Thats a bad thing.)

2. There isnt any useful place for an index on any of these data fields.

Wait a minute! you say, You can place an index on any of these fields! Sure, that is
correct. Of course you can place an index on any of these fields. In fact, you can place an
index on all of them if you really wanted to. But none of those indexes is likely to be
used effectively by MySQL. Why? Well, thats simple. In the schema above, there are
only two values possible for each field a Y or N (or 0 and 1, T and F, etc). Lets
assume a table of 500K auto records. If the only two values in the field are Y and N,
what use would an index be? For a common auto feature, say has_leather. probably
half of the records would contain a Y and half an N. What use would this be to an
index? None. In fact, an index would slow MySQL down, as so many index records
would have to be read, with a lookup operation to the data record from each index record.
The selectivity, or distribution, of key values is extremely low (see my previous article
for more explanation on this) and therefore the index has limited use to MySQL.

3. There is very little flexibility offered by this method.

Imagine if you were to give the customer the ability to add and remove auto features at
will. You would have to GRANT the customer (or the web-based or application user) the
ability to ALTER TABLE. This isnt generally considered to be the most secure or
effective method of managing change

Also, Lets say you came up with a list of 300 auto features. Are you going to add 300
fields to the table? Ever tried doing a SELECT * from a table with 300 fields? Even if
you use the /G option in the mysql client, youd still have a mess!
So, for all the reasons outlined above, I recommend against this approach for all but the simplest
and non-production environments. It isnt flexible enough to withstand change, and the
limitations of performance far outweight any advantage to ease of use.

Wait a Minute!

But wait! you say, MySQL itself uses this strategy for its own mysql database! The
mysql.user table has fields like Select_priv, Insert_priv, and Delete_priv. Dont you think
MySQL knows what theyre doing!?

Yes, of course MySQL knows what theyre doing. But, you have to remember one thing about
the mysql system schema tables. Theyre always in memory. There is no performance
degradation related to the mysql.user tables multiple field definitions for privileges because
upon startup, the MySQL server actually loads all user information (privileges) into a hash table
of structs containing privilege information. When a request is received to perform some
operation, this hash table of user privileges is checked using very fast logical OR and AND
operations. So, there arent any performance issues associated with the mysql tables. This is, by
the way, why you must issue a FLUSH PRIVILEGES when manually changing any of the mysql
tables. The FLUSH PRIVILEGES reloads this in-memory hash table.

So, for the mysql schema, the tables are designed for ease of use and simplicity, so this method
was used to represent privileges. Do I favor it? Not particularly, but I can see why it was done

The INT, BIGINT, or SET Bitmap Method

With this next method, the INT, BIGINT, or MySQL SET data type is used to store from zero to
64 separate key flags within a single field. Again, with this method, we denormalize the schema
by reducing this many-to-many relationship down into a single field in a single table. Below,
youll see an example of our Auto table converted to use the SET data type instead of the 7
distinct CHAR(1) fields from Method #1:


, auto_manufacturer_id SMALLINT UNSIGNED NOT NULL
, auto_model VARCHAR(20) NOT NULL
, asking_price DECIMAL(12,2) UNSIGNED NOT NULL
, auto_options SET('Air Conditioning'
, 'Power Windows'
, 'Power Steering'
, 'Moonroof'
, 'Disk Brakes'
, 'Power Seats'
, 'Leather') NOT NULL
, PRIMARY KEY pk_Auto (auto_id)
Although the SET (and ENUM, SETs one-to-many column type cousin) are listed in the
MySQL manual as string column types, they are internally represented as integers. Below, when
we cover bitwise operations, youll see how you can use the SET data type the same way as you
would an INT or BIGINT, though there are advantages to simply sticking with the SET data type
for simplicitys sake. As with the multiple-field method, there are advantages to the SET method.
They include:

1. You can store a large (64 elements) number of key values in a small storage unit.

For the SET data type, 1 byte of storage is used for up to 8 elements, 2 bytes for up to 16
elements, 3 bytes for up to 24 elements, 4 bytes of storage for up to 32 elements, and 8
bytes of storage for 33-64 possible elements. Compared with method #1 (and method #4
below), this does save storage space in the table

2. MySQL automatically handles showing the descriptive value of the SET column value

With the INT and BIGINT data types, MySQL will simply show the numeric
representation of the field value. With the SET data type, by default, MySQL shows the
descriptive string value of the column, instead of its numeric value (more below). This
can be a handy feature.

3. MySQL provides the handy FIND_IN_SET() function to quickly filter for a single key value.

The FIND_IN_SET() function can be used to query for rows in which a specific key
value is turned on for the particular row. If you want to find whether more than one key
values are turned on, you can use an AND expression in the WHERE clause. For
instance, lets say we wanted to find all records in which the automobile had both the
leather and power windows options. We could use the following:

FROM Auto a
WHERE FIND_IN_SET('Leather', auto_options)>0
AND FIND_IN_SET('Power Windows', auto_options)>0;

Although there are some performance issues with SET fields (see below, on lack of
indexing), the FIND_IN_SET() function is highly optimized to work specifically with the
integer data housed beneath the surface. Bitwise operations are used to determine
whether the row matches the specific key flag queried for. These bitwise operations are
generally very fast, and well cover them below.

Besides FIND_IN_SET(), any bitwise operator that you would normally use on integer data can
be used on SET, INT and BIGINT columns.

Bitwise operations are performed on the actual binary representation of the data. Bits (each
representing a key, are turned on or off by placing the corresponding bit in the binary number to
1. The table below shows the binary and decimal representations of the first byte (8 bits) of an
integer bitmap. As you can see, each on bit simply turns on the appropriate power of 2.
Binary Decimal

0000 0001 1

0000 0010 2

0000 0100 4

0000 1000 8

0001 0000 16

0010 0000 32

0100 0000 64

1000 0000 128

Clearly, the larger the storage unit, the more bits can be used to represent the key flags. A
BIGINT can store up to 64 unique values, an INT 32 unique values, and so on. One advantage to
using the SET type is that it automatically chooses the smallest storage type needed to store the
required number of set elements.

To have more than one key flag turned on within the bitmap field, the flag bits are added
together. Therefore, if the third (23) and fourth (24) flags (0000 0100 and 0000 1000) are turned
on, the bitmap field would contain 0000 1100, or the number 12 in decimal. If ever you want to
see the decimal or binary representation of a SET field column, you can use +0 and the BIN()
function, as shown below:

, auto_options
, auto_options+0 AS 'dec'
, LPAD(BIN(auto_options+0),8,'0') AS 'bin'
FROM Auto a;
| auto_id | auto_options | dec | bin |
| 1 | Power Windows,Power Steering,Moonroof | 14 | 00001110 |
| 2 | Power Windows,Power Steering,Moonroof,Leather | 78 | 01001110 |
| 3 | Power Windows,Moonroof,Leather | 74 | 01001010 |
| 4 | Power Windows,Moonroof | 10 | 00001010 |
4 rows in set (0.00 sec)

Use the LPAD() function to pretty up the output to a pre-determined width, with leading zeroes.

This means that you can do more complex querying using the numeric values of the SET
elements (key values). For instance, suppose we wanted to find all those records which did not
have Leather or Power Steering, but did have Power Windows. From the output above, we can
easily see that the auto with ID#4 is the only record which will meet our criteria.

But, how do we structure our WHERE expression in SQL? Looking back at our table schema,
we know that the numeric position (starting from the number 1) of the key values in our WHERE
clause will be:

Key Value Numeric Position in SET

Leather 7

Power Steering 3

Power Windows 2

Using the MySQL bitwise functions, we can issue our query like so:

SELECT auto_id, auto_options

FROM Auto a
WHERE auto_options & (POW(2,6)+POW(2,2)) = 0
AND auto_options & POW(2,1) > 0;
| auto_id | auto_options |
| 4 | Power Windows,Moonroof |
1 row in set (0.00 sec)

In the code above, the POW() function is used to get the correct bit set for each desired element
in the query. We substract 1 from the number of the element, because the on bits are counted
from the right side of the byte structure and determined as a power of 2. For the first part of the
WHERE expression:

WHERE auto_options & (POW(2,6)+POW(2,2)) = 0

we ensure that the result of the bitwise & operator (which returns a 1 for each bit where both
sides of the equation have the specified bit or bits turned on) results in 0. This ensures that only
rows which do not have the Power Steering and Leather options are returned. The second part of
the WHERE expression uses the bitwise & operator against the Power Windows option, and
filters where the result is greater than zero, so that the resulting rows are known to contain the
Power Windows option.

Besides violating normalization rules (and I wont get into an idealistic debate about that
heretheres more than enough discussion online about that), there are some concrete reasons
why not to use the SET data type for handling many-to-many relationships. Although I began
writing this article quite a while before Sheeri Kritzer posted about the ENUM/SET type, some
points are worth repeating. Foremost among these are the following:

1. Using the SET data type for many-to-many relationships imposes a 64-element limit on the
number of keys available to relate to the main entity.
2. Using the SET data type with the built-in SET functions or any of the bitwise operators prohibits
the MySQL optimizer from using indexes on the column.

This is a major performance drawback to scalability and the primary reason I choose not
to use this data type for anything but the smallest projects or for fields containing 3 or
fewer elements. Why 3 or fewer? Well, because even if an index ould be used against the
SET field, the chances of the index selectivity being large enough to filter an adequate
amount of rows to make the index useful to the optimizer is already very small.

3. Again, working with the SET data type is not particularly flexible.

Changing elements of a SET data type once data is already in the table can be a real pain
in the behind! I wont go into the details, as the MySQL site covers many of the main
points, and Beat Vontobels blog post on SETs covers what the MySQL site doesnt (nice
work, Beat!)

The Long String Method

A third common approach to many-to-many relationships is to use a single long string field to
store essentially a concatenated version of Method #1s multiple fields. An example of this
method would be the schema below:


, auto_manufacturer_id SMALLINT UNSIGNED NOT NULL
, auto_model VARCHAR(20) NOT NULL
, asking_price DECIMAL(12,2) UNSIGNED NOT NULL
, PRIMARY KEY pk_Auto (auto_id)

There are few advantages to this approach, either performance or maintenance-wise, but it can
come in handy in at least one particular circumstance: when application code either relies, or is
made much more simple, by the use of imploded arrays.

Sometimes, particularly if youve inherited some legacy code which is simply too much of a
nuisance to change, it can make sense to work with what youve got. If the application code
relies heavily on returned field values being concatenated string values, this method might work
well. The application returns a long list of either Y or N values representing whether keys are on
or off. For instance, if we had an automobile with the Leather and Power Windows options, and
we used the same order as the SET example above, the returned auto_options field would be:
Perhaps the application was a PHP program which walked the string using something like the

/* We get the $auto_options string from the database... */
$all_options = array(
'Air Conditioning'
, 'Power Windows'
, 'Power Steering'
, 'Moonroof'
, 'Disk Brakes'
, 'Power Seats'
, 'Leather');

$num_options = strlen($auto_options);
for ($i=0;$i<$num_options;++$i) {
if ($auto_options{$i} == 'Y') {
printf("The car has %s", $all_options[$i]);

In this case, the PHP program has the entire key table in memory (the $all_options array). All the
program needs to do is slice up the long string; the position of the character corresponds to the
auto feature in the $all_options array. If there was little complex querying in the application, yet
there were hundreds or thousands of these key values for each row, this might be a decent

Except for this scenario, however, this method is generally not desirable, for the following

1. Too little flexibility

Again, flexibility arises. In this method, what happens if you want to remove an Auto
Feature from the list? Doing so in the PHP code would be fairly simple; just remove the
element from the $all_options array. But, unforunately, once you did that, the offsets into
the character string would be skewed. Likewise, if you wanted to add an Auto Feature,
youd have to change the table schema not a very flexible plan. Making matters worse,
you could only add an Auto Feature to the end of the character string. Adding one in the
middle (say, if you wanted to keep some sort of alphabetical ordering in the PHP code)
would again cause the offsets to be skewed.

2. Performance degrades dramatically because indexes can rarely be used

This disadvantage becomes even more noticeable as queries become more complex. Sure,
an index might be used if you were querying on the first one (or more ordered) Auto
Features. For instance, assuming an index on the auto_options field, the following query
would indeed use an index:
FROM Auto a
WHERE a.auto_options LIKE 'Y%';

But, unfortunately, thats about the limit of an indexes usefulness for this method. In the
example above, we could use an index to find all records having Air Conditioning. But,
what happens when we want to, say, find all automobiles which have Power Seats and
Power Windows (the sixth and second keys)? Now were looking at the following SQL

FROM Auto a
WHERE SUBSTRING(a.auto_options, 6,1) = 'Y'
AND SUBSTRING(a.auto_options, 2,1) = 'Y';

Besides being a mess, this code will never be able to use an index because of the use of
the SUBSTRING() function on the left side of the WHERE equations. And, because
indexes wont be used, the performance of this schema will not scale well at all.

The Mapping Table Method

Alright, the final (and my preferred) method for managing many-to-many relationships is to use
a key mapping table (sometimes called a relationship table). In the beginning of the article, in
figure C, you saw an E-R diagram showing a key mapping table relating the AutoFeature entity
to the Auto entity. Note that this method is the only truly normalized method of managing many-
to-many relationships. Up until now in the article, the schema organization has been in a single
table. Now, through the use of three distinct tables, we are able to normalize the schema into the
following DDL:


, auto_manufacturer_id SMALLINT UNSIGNED NOT NULL
, auto_model VARCHAR(20) NOT NULL
, asking_price DECIMAL(12,2) UNSIGNED NOT NULL
, PRIMARY KEY pk_Auto (auto_id)

CREATE TABLE AutoFeature (

, feature_name VARCHAR(80) NOT NULL
, PRIMARY KEY pk_AutoFeature (auto_feature_id)

CREATE TABLE Auto2AutoFeature (

, PRIMARY KEY pk_Auto2AutoFeature (auto_id, auto_feature_id)
Before we discuss the advantages to this method, its worth pointing out a couple drawbacks to
storing many-to-many relationships in this normalized fashion.

1. Generally, the key mapping table method will use the most overall storage of any of these
described approaches.

As with all matters in the database design world, everything comes with a tradeoff. The
key mapping table is no exception. The biggest tradeoff by far is the storage space needed
to store the many-to-many relationship. Instead of a single field, or multiple small fields,
in a single table, we now must store the foreign keys of each entity multiple times, with
each unique combination occupying a single row in the relationship table.

Additionally, because indexes can be effectively used against the key mapping table, we
now need space to store the index records as well as the data records. As with any index,
performance of INSERT and UPDATE operations (especially on high-volume mixed
OLTP/OLAP applications) can suffer. However, any performance impact on INSERT
and UPDATE operations is usually far outweighed by the performance benefits for
SELECT operations. As always, however, benchmarking and testing is always a good
idea; not only in the beginning of the project, but also at timed intervals as the database
grows and matures.

2. There are now one or two more tables to maintain for the schema

Whilst having an extra table or two will provide us with the most flexibility, that
flexibility comes at the cost of extra tables which must be maintained by both the
application and the database administrator. For just a few seldom-changing keys, the
hassle of maintaining extra tables and relationships may not be worth the added

Now, on to the benefits of this approach, along with some examples of how to retrieve result sets
using the key mapping table.

1. Robust Indexing possibilities are now supported.

Because both sides of the many-to-many relationship are separate fields in distinct rows
of the key mapping table, our queries can support various indexing strategies. Its best to
see this in action, so Ill demonstrate with two simple query examples.

Lets assume that we wish to find all the options available, in a list, for a specific
automobile. This is a fairly simple query, but a good starter:

SELECT af.feature_name
FROM AutoFeature af
INNER JOIN Auto2AutoFeature a2af
ON af.auto_feature_id = a2af.auto_feature_id
WHERE a2af.auto_id = 7;
| feature_name |
| Air Conditioning |
| Disk Brakes |
2 rows in set (0.00 sec)

Pretty simple query. Just an INNER JOIN from the AutoFeature table into our key
mapping table on the auto_feature_id column, then a WHERE condition specifying the
needed vehicles ID. This kind of output isnt possible with the previous 3 methods
without a lot of headache, but lets face it: this is generally the format that an application
needs data, correct? In a list, or an array. Now, with a simple query like this, that kind of
output is easy.

But, are our indexes in operation for the above query? Lets find out:

mysql> EXPLAIN SELECT af.feature_name

-> FROM AutoFeature af
-> INNER JOIN Auto2AutoFeature a2af
-> ON af.auto_feature_id = a2af.auto_feature_id
-> WHERE a2af.auto_id = 7 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: a2af
type: ref
possible_keys: PRIMARY
key_len: 4
ref: const
rows: 2
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: af
type: eq_ref
possible_keys: PRIMARY
key_len: 2
ref: test.a2af.auto_feature_id
rows: 1
2 rows in set (0.00 sec)

Indeed, they are. In the EXPLAIN output above, youll see that the optimizer is using a
constant (ref: const) on the PRIMARY KEY to filter records. In the Extra column,
youll note that MySQL helpfully tells us that its Using index. Many novice (or even
intermediate) database developers new to MySQL will assume that if they see Using
index in the Extra column, that they have an optimal query plan in place for the SQL
code. This is actually a bit of a misnomer. When you see Using index in the Extra
column, it means that MySQL is able to use the index records (as opposed to the data
records which the index is attached to) in order to complete the query. In other words,
MySQL doesnt have to even go to the data records; everything it needs for the query is
covered by the index records. This situation is called a covering index, and is something
that goes hand-in-hand with key mapping tables. Why? Because, frankly, the entire table
IS the index!

For this reason, and others, which well get to below, key mapping tables, when properly
structured, are ideal for joining operations. In EXPLAIN plans for queries which use the
key mapping table, you will often see the use of a covering index, because all of the
joined columns are available in the index records. Practically speaking, this means that
for MyISAM tables, the key_buffer will contain all the information that the query needs
already in RAM; there is no need to access the data in the .MYD file, saving disk
accesses and making the query performance lightning fast. For InnoDB tables, the access
is just as quick. The PRIMARY KEY will be housed in the InnoDB data page buffer pool
(since it is a clustered index and is the actual data records). So, likewise, queries will be
lightning fast as the records are in memory

So, what about other types of queries; do they also benefit from the PRIMARY KEY
index? Lets find out. Heres another fairly simple query which attempts to find all
automobiles having the Leather option:

FROM Auto a
INNER JOIN Auto2AutoFeature a2af
ON a.auto_id = a2af.auto_id
WHERE a2af.auto_feature_id = 7;
| auto_id | auto_manufacturer_id | auto_model | model_year |
asking_price |
| 3 | 1 | 1 | 2003 |
5000.00 |
| 4 | 1 | 2 | 2005 |
38000.00 |
| 5 | 1 | 2 | 2004 |
31000.00 |
| 6 | 1 | 3 | 2003 |
10000.00 |
| 8 | 2 | 1 | 2004 |
12000.00 |
| 10 | 2 | 2 | 2005 |
32000.00 |
| 11 | 2 | 2 | 2005 |
42000.00 |
7 rows in set (0.00 sec)

Lets use an EXPLAIN to see if weve indeed got an ideal execution plan:
-> FROM Auto a
-> INNER JOIN Auto2AutoFeature a2af
-> ON a.auto_id = a2af.auto_id
-> WHERE a2af.auto_feature_id = 7 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: a
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 16
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: a2af
type: eq_ref
possible_keys: PRIMARY
key_len: 6
ref: test.a.auto_id,const
rows: 1
Extra: Using index
2 rows in set (0.00 sec)

Uh oh! What happened? Weve encountered the dreaded ALL access type! As you can
see, MySQL chooses to do a table scan of the Auto table, and for each auto_id, do a
lookup into the Auto2AutoFeature table along the auto_id key, filtering on the
Auto2AutoFeature PRIMARY KEYs auto_feature_id column (Look for ref:
test.a.auto_id,const). But, you ask, why didnt MySQL just find the auto_id records in
Auto2AutoFeature that had the Leather option, and then join that smaller resultset to the
Auto table?

This is where the robust indexing strategies I spoke about earlier come into play, and
where many novices get tripped up when dealing with key mapping tables.

The reason that MySQL chose the access plan above is because the auto_feature_id
column (on which we are supplying a constant filter value of 7) is on the right side of the
PRIMARY KEY index. In order for MySQL to use an index effectively, it must be able
to apply a constant or range filter value to the columns of an index, from LEFT to
RIGHT. This is why, although you do see the term Using index in the Extra column of
the second EXPLAIN, MySQL actually has chosen a sub-optimal plan. The solution? We
add another index to the key mapping table which allows the reverse join direction to be

CREATE UNIQUE INDEX ix_ReversePK ON Auto2AutoFeature (auto_feature_id,

Now, lets take another stap at the same EXPLAIN from above:


-> FROM Auto a
-> INNER JOIN Auto2AutoFeature a2af
-> ON a.auto_id = a2af.auto_id
-> WHERE a2af.auto_feature_id = 7 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: a2af
type: ref
possible_keys: PRIMARY,ix_ReversePK
key: ix_ReversePK
key_len: 2
ref: const
rows: 7
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: a
type: eq_ref
possible_keys: PRIMARY
key_len: 4
ref: test.a2af.auto_id
rows: 1
2 rows in set (0.00 sec)

See the difference?! Now, the new reverse direction index we just added is first used to
filter the Auto2AutoFeature records, and THEN the Auto table is joined. This is the
optimal query plan for this type of query.

So, to sum up this advantage, it should be said that with key mapping tables, youre given
much more ability to tune and optimize your queries, but at the same time, youve got to
know what youre looking for, and got to be willing to put in the time to analyze your
queries. But, hey! If youve read all the way through this article so far, Im willing to bet
youll do just that.

2. Flexibility to add and remove elements easily, and without schema changes.

One fatal flaw of the previously covered methods is their inability to deal easily with
change. Lets face it, change happens constantly in the business world. If youre
designing applications for use in this world, you had better make them able to deal with
constant change. Otherwise, you will be on the phone 24/7 fulfilling request after request
to make small changes to this type of data. Do yourself the favor, and give yourself back
that time from the start.
With key mapping tables, adding new elements to the many-to-many relationship is
simple. You just add a record to the master table:

INSERT INTO AutoFeature (feature_name) VALUES ('GPS Navigation');

All done. If you want to remove one, simply issue a DELETE with a join into the key
mapping table. This will remove the parent and all child relationships:

DELETE AutoFeature, Auto2AutoFeature

FROM AutoFeature
INNER JOIN Auto2AutoFeature
ON AutoFeature.auto_feature_id = Auto2AutoFeature.auto_feature_id
WHERE AutoFeature.auto_feature_id = 4;
Query OK, 5 rows affected (0.00 sec)

That wraps up this parts coverage of the key mapping technique. It was just a brief overview.
Next time, well cover how to get the most out of your key mapping tables, including how to
structure complex joins for various query filters against the many-to-many keys.

Entity-relationship Diagram

An entity-relationship (ER) diagram is a specialized graphic that illustrates the interrelationships

between entities in a database. ER diagrams often use symbols to represent three different types
of information. Boxes are commonly used to represent entities. Diamonds are normally used to
represent relationships and ovals are used to represent attributes. Here is an ERD depicting the
relationships declared above:
UML Data Modeling

The Unified Modeling Language (UML) diagram describes the structure of a system by showing
the system's classes, their attributes, and the relationships between the classes. It is the standard
language for modeling business and software application requirements. Its flexibility allows for
many different types of modeling, such as understanding the business process, workflow of
events, sequence of queries, applications, databases, architectures, and more. Since it can be used
for both Object-oriented class design and to identify database table relationships, it makes data
attributes much easier to map between the class objects and the underlying database tables that
store them.

Whereas ER models present a conceptual model of the entity relationships, UML diagrams
present them in a more physical form. In fact, there are a number of software products that can
create a database from the UML diagram.

To create a UML diagram:

1. Begin by listing every table as a box with their attributes (fields) listed inside:

2. Next, link primary and foreign keys together by drawing a connecting line.

3. Include the cardinality above the line, next to the attribute:

4. Add junction tables for many-to-many relationships. Recall that the stock and category
tables have a many-to-many relationship. As such, we cant link them directly. Instead, we have
to create a junction table called stock_category, which will then have two one-to-many
relationships, using each table primary key as foreign keys:

More about Cardinality

There are several ways to denote cardinality in UML diagrams. None are more correct than
another. Its simply a matter of what youre accustomed to.

In the example above, we used Chen notation. It uses a numeric one (1) indicates the one in a
one-to-one or one-to-many relationship. The many can be indicated using an n or infinity
symbol ().
In UML associations, multiplicity is used on those associations to denote cardinality, using the
following conventions:

Left Right Example

1 1 one-to-one person <-> weight
0..1 1 optional on one side one-to-one date of death <-> person
0..* or * 0..* or * optional on both sides many-to-many person <-> book
1 1..* one-to-many person <-> language

Chen notation is more prevalent in the United States, while Crow's Foot notation is used
primarily in the UK. It uses the ends of the relationship lines to represent the cardinality of the


Knowing how to establish table cardinality is a key skill in database design because it can
identify areas where normalization may not be optimal or was incorrectly applied. Usually, when
related tables cannot be mapped using one of the three relationship types covered today, it means
that you have to take a step back and reconsider the break down of the entities. Often times, a
table will still contain overlapping data, which needs to be broken down further.

Adding User Accounts

You can create MySQL accounts two ways:

By using account-management statements intended for creating accounts and establishing

their privileges, such as CREATE USER and GRANT. These statements cause the server to
make appropriate modifications to the underlying grant tables.
By manipulating the MySQL grant tables directly with statements such as INSERT,

The preferred method is to use account-management statements because they are more concise
and less error-prone than manipulating the grant tables directly. All such statements are
described in Section 14.7.1, Account Management Statements. Direct grant table manipulation
is discouraged, and is not described here. The server is free to ignore rows that become
malformed as a result of such modifications.

Another option for creating accounts is to use the GUI tool MySQL Workbench. Also, several
third-party programs offer capabilities for MySQL account administration. phpMyAdmin is one
such program.
The following examples show how to use the mysql client program to set up new accounts.
These examples assume that privileges have been set up according to the defaults described in
Section 2.10.4, Securing the Initial MySQL Accounts. This means that to make changes, you
must connect to the MySQL server as the MySQL root user, which has the CREATE USER

First, use the mysql program to connect to the server as the MySQL root user:

shell> mysql --user=root mysql

If you have assigned a password to the root account, you must also supply a --password or -p

After connecting to the server as root, you can add new accounts. The following example uses
CREATE USER and GRANT statements to set up four accounts:

mysql> CREATE USER 'finley'@'localhost' IDENTIFIED BY 'some_pass';

mysql> GRANT ALL PRIVILEGES ON *.* TO 'finley'@'localhost'
mysql> CREATE USER 'finley'@'%' IDENTIFIED BY 'some_pass';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'finley'@'%'
mysql> CREATE USER 'admin'@'localhost' IDENTIFIED BY 'admin_pass';
mysql> GRANT RELOAD,PROCESS ON *.* TO 'admin'@'localhost';
mysql> CREATE USER 'dummy'@'localhost';

The accounts created by those statements have the following properties:

Two accounts have a user name of finley and a password of some_pass. Both are
superuser accounts with full privileges to do anything. The 'finley'@'localhost'
account can be used only when connecting from the local host. The 'finley'@'%'
account uses the '%' wildcard for the host part, so it can be used to connect from any

The 'finley'@'localhost' account is necessary if there is an anonymous-user account

for localhost. Without the 'finley'@'localhost' account, that anonymous-user
account takes precedence when finley connects from the local host and finley is
treated as an anonymous user. The reason for this is that the anonymous-user account has
a more specific Host column value than the 'finley'@'%' account and thus comes
earlier in the user table sort order. (user table sorting is discussed in Section 7.2.4,
Access Control, Stage 1: Connection Verification.)

The 'admin'@'localhost' account has a password of admin_pass. This account can be

used only by admin to connect from the local host. It is granted the RELOAD and PROCESS
administrative privileges. These privileges enable the admin user to execute the
mysqladmin reload, mysqladmin refresh, and mysqladmin flush-xxx commands, as
well as mysqladmin processlist . No privileges are granted for accessing any databases.
You could add such privileges using GRANT statements.
The 'dummy'@'localhost' account has no password (which is insecure and not
recommended). This account can be used only to connect from the local host. No
privileges are granted. It is assumed that you will grant specific privileges to the account
using GRANT statements.

To see the privileges for an account, use SHOW GRANTS:

mysql> SHOW GRANTS FOR 'admin'@'localhost';

| Grants for admin@localhost |
| GRANT RELOAD, PROCESS ON *.* TO 'admin'@'localhost' |

To see nonprivilege properties for an account, use SHOW CREATE USER:

mysql> SHOW CREATE USER 'admin'@'localhost'\G

*************************** 1. row ***************************
CREATE USER for admin@localhost: CREATE USER 'admin'@'localhost'
IDENTIFIED WITH 'mysql_native_password'
AS '*67ACDEBDAB923990001F0FFB017EB8ED41861105'

The next examples create three accounts and grant them access to specific databases. Each of
them has a user name of custom and password of obscure:

mysql> CREATE USER 'custom'@'localhost' IDENTIFIED BY 'obscure';

-> ON bankaccount.*
-> TO 'custom'@'localhost';
mysql> CREATE USER 'custom'@'' IDENTIFIED BY 'obscure';
-> ON expenses.*
-> TO 'custom'@'';
mysql> CREATE USER 'custom'@'' IDENTIFIED BY 'obscure';
-> ON customer.*
-> TO 'custom'@'';

The three accounts can be used as follows:

The first account can access the bankaccount database, but only from the local host.
The second account can access the expenses database, but only from the host
The third account can access the customer database, from any host in the
domain. This account has access from all machines in the domain due to use of the %
wildcard character in the host part of the account name.

Privileges Provided by MySQL

MySQL provides privileges that apply in different contexts and at different levels of operation:

Administrative privileges enable users to manage operation of the MySQL server. These
privileges are global because they are not specific to a particular database.
Database privileges apply to a database and to all objects within it. These privileges can
be granted for specific databases, or globally so that they apply to all databases.
Privileges for database objects such as tables, indexes, views, and stored routines can be
granted for specific objects within a database, for all objects of a given type within a
database (for example, all tables in a database), or globally for all objects of a given type
in all databases).

Information about account privileges is stored in the user, db, tables_priv, columns_priv,
and procs_priv tables in the mysql database (see Section 7.2.2, Grant Tables). The MySQL
server reads the contents of these tables into memory when it starts and reloads them under the
circumstances indicated in Section 7.2.6, When Privilege Changes Take Effect. Access-control
decisions are based on the in-memory copies of the grant tables.

Some releases of MySQL introduce changes to the structure of the grant tables to add new
privileges or features. To make sure that you can take advantage of any new capabilities, update
your grant tables to have the current structure whenever you update to a new version of MySQL.
See Section 5.4.7, mysql_upgrade Check and Upgrade MySQL Tables.

The following table shows the privilege names used at the SQL level in the GRANT and REVOKE
statements, along with the column name associated with each privilege in the grant tables and the
context in which the privilege applies.

Table 7.2 Permissible Privileges for GRANT and REVOKE

Privilege Column Context

CREATE Create_priv databases, tables, or indexes

DROP Drop_priv databases, tables, or views

GRANT OPTION Grant_priv databases, tables, or stored routines

LOCK TABLES Lock_tables_priv databases

REFERENCES References_priv databases or tables

EVENT Event_priv databases

Privilege Column Context

ALTER Alter_priv tables

DELETE Delete_priv tables

INDEX Index_priv tables

INSERT Insert_priv tables or columns

SELECT Select_priv tables or columns

UPDATE Update_priv tables or columns

CREATE TEMPORARY TABLES Create_tmp_table_priv tables

TRIGGER Trigger_priv tables

CREATE VIEW Create_view_priv views

SHOW VIEW Show_view_priv views

ALTER ROUTINE Alter_routine_priv stored routines

CREATE ROUTINE Create_routine_priv stored routines

EXECUTE Execute_priv stored routines

FILE File_priv file access on server host

CREATE TABLESPACE Create_tablespace_priv server administration

CREATE USER Create_user_priv server administration

PROCESS Process_priv server administration

PROXY see proxies_priv table server administration

RELOAD Reload_priv server administration

REPLICATION CLIENT Repl_client_priv server administration

Privilege Column Context

REPLICATION SLAVE Repl_slave_priv server administration

SHOW DATABASES Show_db_priv server administration

SHUTDOWN Shutdown_priv server administration

SUPER Super_priv server administration

ALL [PRIVILEGES] server administration

USAGE server administration

The following list provides a general description of each privilege available in MySQL.
Particular SQL statements might have more specific privilege requirements than indicated here.
If so, the description for the statement in question provides the details.

The ALL or ALL PRIVILEGES privilege specifier is shorthand. It stands for all privileges
available at a given privilege level (except GRANT OPTION). For example, granting ALL
at the global or table level grants all global privileges or all table-level privileges.
The ALTER privilege enables use of ALTER TABLE to change the structure of tables. ALTER
TABLE also requires the CREATE and INSERT privileges. Renaming a table requires ALTER
and DROP on the old table, CREATE, and INSERT on the new table.
The ALTER ROUTINE privilege is needed to alter or drop stored routines (procedures and
The CREATE privilege enables creation of new databases and tables.
The CREATE ROUTINE privilege is needed to create stored routines (procedures and
The CREATE TABLESPACE privilege is needed to create, alter, or drop tablespaces and log
file groups.
The CREATE TEMPORARY TABLES privilege enables the creation of temporary tables using

After a session has created a temporary table, the server performs no further privilege
checks on the table. The creating session can perform any operation on the table, such as

One implication of this behavior is that a session can manipulate its temporary tables
even if the current user has no privilege to create them. Suppose that the current user does
not have the CREATE TEMPORARY TABLES privilege but is able to execute a DEFINER-
context stored procedure that executes with the privileges of a user who does have
CREATE TEMPORARY TABLES and that creates a temporary table. While the procedure
executes, the session uses the privileges of the defining user. After the procedure returns,
the effective privileges revert to those of the current user, which can still see the
temporary table and perform any operation on it.

To keep privileges for temporary and nontemporary tables separate, a common

workaround for this situation is to create a database dedicated to the use of temporary
tables. Then for that database, a user can be granted the CREATE TEMPORARY TABLES
privilege, along with any other privileges required for temporary table operations done by
that user.

The CREATE USER privilege enables use of ALTER USER, CREATE USER, DROP USER,
The CREATE VIEW privilege enables use of CREATE VIEW.
The DELETE privilege enables rows to be deleted from tables in a database.
The DROP privilege enables you to drop (remove) existing databases, tables, and views.
The DROP privilege is required in order to use the statement ALTER TABLE ... DROP
PARTITION on a partitioned table. The DROP privilege is also required for TRUNCATE
TABLE. If you grant the DROP privilege for the mysql database to a user, that user can
drop the database in which the MySQL access privileges are stored.
The EVENT privilege is required to create, alter, drop, or see events for the Event
The EXECUTE privilege is required to execute stored routines (procedures and functions).
The FILE privilege gives you permission to read and write files on the server host using
the LOAD DATA INFILE and SELECT ... INTO OUTFILE statements and the
LOAD_FILE() function. A user who has the FILE privilege can read any file on the server
host that is either world-readable or readable by the MySQL server. (This implies the
user can read any file in any database directory, because the server can access any of
those files.) The FILE privilege also enables the user to create new files in any directory
where the MySQL server has write access. This includes the server's data directory
containing the files that implement the privilege tables. As a security measure, the server
will not overwrite existing files.

To limit the location in which files can be read and written, set the secure_file_priv
system to a specific directory. See Section 6.1.4, Server System Variables.

The GRANT OPTION privilege enables you to give to other users or remove from other
users those privileges that you yourself possess.
The INDEX privilege enables you to create or drop (remove) indexes. INDEX applies to
existing tables. If you have the CREATE privilege for a table, you can include index
definitions in the CREATE TABLE statement.
The INSERT privilege enables rows to be inserted into tables in a database. INSERT is also
maintenance statements.
The LOCK TABLES privilege enables the use of explicit LOCK TABLES statements to lock
tables for which you have the SELECT privilege. This includes the use of write locks,
which prevents other sessions from reading the locked table.
The PROCESS privilege pertains to display of information about the threads executing
within the server (that is, information about the statements being executed by sessions).
The privilege enables use of SHOW PROCESSLIST or mysqladmin processlist to see
threads belonging to other accounts; you can always see your own threads. The PROCESS
privilege also enables use of SHOW ENGINE.
The PROXY privilege enables a user to impersonate or become known as another user. See
Section 7.3.9, Proxy Users.
The REFERENCES privilege is unused before MySQL 5.7.6. As of 5.7.6, creation of a
foreign key constraint requires the REFERENCES privilege for the parent table.
The RELOAD privilege enables use of the FLUSH statement. It also enables mysqladmin
commands that are equivalent to FLUSH operations: flush-hosts, flush-logs, flush-
privileges, flush-status, flush-tables, flush-threads, refresh, and reload.

The reload command tells the server to reload the grant tables into memory. flush-
privileges is a synonym for reload. The refresh command closes and reopens the log
files and flushes all tables. The other flush-xxx commands perform functions similar to
refresh, but are more specific and may be preferable in some instances. For example, if
you want to flush just the log files, flush-logs is a better choice than refresh.

The REPLICATION CLIENT privilege enables the use of SHOW MASTER STATUS, SHOW
The REPLICATION SLAVE privilege should be granted to accounts that are used by slave
servers to connect to the current server as their master. Without this privilege, the slave
cannot request updates that have been made to databases on the master server.
The SELECT privilege enables you to select rows from tables in a database. SELECT
statements require the SELECT privilege only if they actually retrieve rows from a table.
Some SELECT statements do not access tables and can be executed without permission for
any database. For example, you can use SELECT as a simple calculator to evaluate
expressions that make no reference to tables:

The SELECT privilege is also needed for other statements that read column values. For
example, SELECT is needed for columns referenced on the right hand side of
col_name=expr assignment in UPDATE statements or for columns named in the WHERE
clause of DELETE or UPDATE statements.

The SHOW DATABASES privilege enables the account to see database names by issuing the
SHOW DATABASE statement. Accounts that do not have this privilege see only databases
for which they have some privileges, and cannot use the statement at all if the server was
started with the --skip-show-database option. Note that any global privilege is a
privilege for the database.
The SHOW VIEW privilege enables use of SHOW CREATE VIEW.
The SHUTDOWN privilege enables use of the SHUTDOWN statement, the mysqladmin
shutdown command, and the mysql_shutdown() C API function.
The SUPER privilege enables an account to use CHANGE MASTER TO, KILL or mysqladmin
kill to kill threads belonging to other accounts (you can always kill your own threads),
PURGE BINARY LOGS, configuration changes using SET GLOBAL to modify global system
variables, the mysqladmin debug command, enabling or disabling logging, performing
updates even if the read_only system variable is enabled, starting and stopping
replication on slave servers, specification of any account in the DEFINER attribute of
stored programs and views, and enables you to connect (once) even if the connection
limit controlled by the max_connections system variable is reached.

To create or alter stored functions if binary logging is enabled, you may also need the
SUPER privilege, as described in Section 21.7, Binary Logging of Stored Programs.

The TRIGGER privilege enables trigger operations. You must have this privilege for a
table to create, drop, execute, or display triggers for that table.

When a trigger is activated (by a user who has privileges to execute INSERT, UPDATE, or
DELETE statements for the table associated with the trigger), trigger execution requires
that the user who defined the trigger still have the TRIGGER privilege.

The UPDATE privilege enables rows to be updated in tables in a database.

The USAGE privilege specifier stands for no privileges. It is used at the global level with
GRANT to modify account attributes such as resource limits or SSL characteristics without
affecting existing account privileges.

It is a good idea to grant to an account only those privileges that it needs. You should exercise
particular caution in granting the FILE and administrative privileges:

The FILE privilege can be abused to read into a database table any files that the MySQL
server can read on the server host. This includes all world-readable files and files in the
server's data directory. The table can then be accessed using SELECT to transfer its
contents to the client host.
The GRANT OPTION privilege enables users to give their privileges to other users. Two
users that have different privileges and with the GRANT OPTION privilege are able to
combine privileges.
The ALTER privilege may be used to subvert the privilege system by renaming tables.
The SHUTDOWN privilege can be abused to deny service to other users entirely by
terminating the server.
The PROCESS privilege can be used to view the plain text of currently executing
statements, including statements that set or change passwords.
The SUPER privilege can be used to terminate other sessions or change how the server
Privileges granted for the mysql database itself can be used to change passwords and
other access privilege information. Passwords are stored encrypted, so a malicious user
cannot simply read them to know the plain text password. However, a user with write
access to the user table authentication_string column can change an account's
password, and then connect to the MySQL server using that account. DROP USER Syntax

DROP USER [IF EXISTS] user [, user] ...

The DROP USER statement removes one or more MySQL accounts and their privileges. It
removes privilege rows for the account from all grant tables.

To use DROP USER, you must have the global CREATE USER privilege or the DELETE privilege for
the mysql database. When the read_only system variable is enabled, DROP USER additionally
requires the SUPER privilege.

An error occurs if you try to drop an account that does not exist.

As of MySQL 5.7.8, the IF EXISTS clause can be used, which causes the statement to produce a
warning for each named account that does not exist, rather than an error.

Each account name uses the format described in Section 7.2.3, Specifying Account Names.
For example:

DROP USER 'jeffrey'@'localhost';

If you specify only the user name part of the account name, a host name part of '%' is used.


DROP USER does not automatically close any open user sessions. Rather, in the event that a user
with an open session is dropped, the statement does not take effect until that user's session is
closed. Once the session is closed, the user is dropped, and that user's next attempt to log in will
fail. This is by design.

DROP USER does not automatically drop or invalidate databases or objects within them that the
old user created. This includes stored programs or views for which the DEFINER attribute names
the dropped user. Attempts to access such objects may produce an error if they execute in definer
security context. (For information about security context, see Section 21.6, Access Control for
Stored Programs and Views.)

I created user user@'%' with password 'password. But I can not connect with:
47 down vote
favorite mysql_connect('localhost:3306', 'user', 'password');

32 When I created user user@'localhost', I was able to connect. Why? Doesn't

'%' mean from ANY host?
mysql remote-access

edited Jul 28 '15 at 9:59 asked Apr 29 '13 at 20:24

shareimprove this question

Saty user2333586

17.7k51733 243144

add a comment

3 Answers
active oldest votes

In order to connect remotely you have to have MySQL bind port: 3306 to your
machines IP in my.cnf. Then you have to have created the user in both localhost
and '%' wildcard and grant permissions on all DB's as such . See below:


#Replace xxx with your IP Address

bind-address =

up vote 136 then

down vote
accepted CREATE USER 'myuser'@'localhost' IDENTIFIED BY 'mypass';
CREATE USER 'myuser'@'%' IDENTIFIED BY 'mypass';


GRANT ALL ON *.* TO 'myuser'@'localhost';

GRANT ALL ON *.* TO 'myuser'@'%';

Depending on your OS you may have to open port 3306 to allow remote
Did you find this question interesting? Try our newsletter

Sign up for our newsletter and get our top new questions delivered to your inbox (see an

for what DB is the user? look at this example

mysql> create database databasename;

Query OK, 1 row affected (0.00 sec)
mysql> grant all on databasename.* to cmsuser@localhost identified by
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

so to return to you question the "%" operator means all computers in your network.

like aspesa shows I'm also sure that you have to create or update a user. look for all
your mysql users:

SELECT user,password,host FROM user;

up vote
4 down as soon as you got your user set up you should be able to connect like this:
mysql -h localhost -u gmeier -p

hope it helps

answered Apr 29 '13 at 20:29

shareimprove this answer edited Apr 29 '13 at 21:21



generally, for all db. I just want to connect, not to select DB.
codemysql_connect('localhost:3306', 'user', 'password'); code user2333586 Apr 29 '13
at 20:54

what OS are you on, Windows, Linux?? apesa Apr 29 '13 at 21:01
windows. wamp server. user2333586 Apr 29 '13 at 21:44

You will need to make sure your IP address is bound to port 3306. In windows you might
use netstat -n to see what ports are bound to your IP. If you see you will
not be able to connect from anything other than localhost apesa Apr 29 '13 at 23:10

add a comment

Follow instructions (steps 1 to 3 don't needed in windows):

1. Find mysql config to edit:

/etc/mysql/my.cnf (Mysql 5.5)

/etc/mysql/conf.d/mysql.cnf (Mysql 5.6+)

up vote 2 2. Find bind-address= in config file change bind-address=

down (you can set bind address to one of your interface ips or like me use
vote 3. Restart mysql service run on console: service restart mysql
4. Create a user with a safe password for remote connection. To do this run
following command in mysql (if you are linux user to reach mysql console run
mysql and if you set password for root run mysql -p):
6. ON *.* TO 'remote'@'%'
7. IDENTIFIED BY 'safe_password'

Now you should have a user with name of user and password of safe_password with
capability of remote connect.

You will use following commandline to connect -

mysql -u<user-id> -p<password> -h<your-hostname-or-ipaddress>

2 down For applications running on different machines trying to connect to your database, you
vote only need to replace 'localhost' with your machine's hostname or ipaddress.

In, general if you are able to ping your machine from a different system, your database
can be connected to from that machine, just use whatever name you used for 'pinging' in
place of localhost.