3.6 Examples of Common Queries

Here are examples of how to solve some common problems with MySQL.

Some of the examples use the table 'shop' to hold the price of each article (item number) for certain traders (dealers). Supposing that each trader has a single fixed price per article, then ('article', 'dealer') is a primary key for the records.

Start the command-line tool *note 'mysql': mysql. and select a database:

 $> mysql YOUR-DATABASE-NAME

To create and populate the example table, use these statements:

 CREATE TABLE shop (
     article INT UNSIGNED  DEFAULT '0000' NOT NULL,
     dealer  CHAR(20)      DEFAULT ''     NOT NULL,
     price   DECIMAL(16,2) DEFAULT '0.00' NOT NULL,
     PRIMARY KEY(article, dealer));
 INSERT INTO shop VALUES
     (1,'A',3.45),(1,'B',3.99),(2,'A',10.99),(3,'B',1.45),
     (3,'C',1.69),(3,'D',1.25),(4,'D',19.95);

After issuing the statements, the table should have the following contents:

 SELECT * FROM shop ORDER BY article;

 +---------+--------+-------+
 | article | dealer | price |
 +---------+--------+-------+
 |       1 | A      |  3.45 |
 |       1 | B      |  3.99 |
 |       2 | A      | 10.99 |
 |       3 | B      |  1.45 |
 |       3 | C      |  1.69 |
 |       3 | D      |  1.25 |
 |       4 | D      | 19.95 |
 +---------+--------+-------+

 File: manual.info.tmp, Node: example-maximum-column, Next: example-maximum-row, Prev: examples, Up: examples

3.6.1 The Maximum Value for a Column

'What is the highest item number?'

 SELECT MAX(article) AS article FROM shop;

 +---------+
 | article |
 +---------+
 |       4 |
 +---------+

 File: manual.info.tmp, Node: example-maximum-row, Next: example-maximum-column-group, Prev: example-maximum-column, Up: examples

3.6.2 The Row Holding the Maximum of a Certain Column

Task: Find the number, dealer, and price of the most expensive article.

This is easily done with a subquery:

 SELECT article, dealer, price
 FROM   shop
 WHERE  price=(SELECT MAX(price) FROM shop);

 +---------+--------+-------+
 | article | dealer | price |
 +---------+--------+-------+
 |    0004 | D      | 19.95 |
 +---------+--------+-------+

Another solution is to use a 'LEFT JOIN', as shown here:

 SELECT s1.article, s1.dealer, s1.price
 FROM shop s1
 LEFT JOIN shop s2 ON s1.price < s2.price
 WHERE s2.article IS NULL;

You can also do this by sorting all rows descending by price and get only the first row using the MySQL-specific 'LIMIT' clause, like this:

 SELECT article, dealer, price
 FROM shop
 ORDER BY price DESC
 LIMIT 1;

Note:

If there were several most expensive articles, each with a price of 19.95, the 'LIMIT' solution would show only one of them.

 File: manual.info.tmp, Node: example-maximum-column-group, Next: example-maximum-column-group-row, Prev: example-maximum-row, Up: examples

3.6.3 Maximum of Column per Group

Task: Find the highest price per article.

 SELECT article, MAX(price) AS price
 FROM   shop
 GROUP BY article
 ORDER BY article;

 +---------+-------+
 | article | price |
 +---------+-------+
 |    0001 |  3.99 |
 |    0002 | 10.99 |
 |    0003 |  1.69 |
 |    0004 | 19.95 |
 +---------+-------+

 File: manual.info.tmp, Node: example-maximum-column-group-row, Next: example-user-variables, Prev: example-maximum-column-group, Up: examples

3.6.4 The Rows Holding the Group-wise Maximum of a Certain Column

Task: For each article, find the dealer or dealers with the most expensive price.

This problem can be solved with a subquery like this one:

 SELECT article, dealer, price
 FROM   shop s1
 WHERE  price=(SELECT MAX(s2.price)
               FROM shop s2
               WHERE s1.article = s2.article)
 ORDER BY article;

 +---------+--------+-------+
 | article | dealer | price |
 +---------+--------+-------+
 |    0001 | B      |  3.99 |
 |    0002 | A      | 10.99 |
 |    0003 | C      |  1.69 |
 |    0004 | D      | 19.95 |
 +---------+--------+-------+

The preceding example uses a correlated subquery, which can be inefficient (see *note correlated-subqueries::). Other possibilities for solving the problem are to use an uncorrelated subquery in the 'FROM' clause or a 'LEFT JOIN'.

Uncorrelated subquery:

 SELECT s1.article, dealer, s1.price
 FROM shop s1
 JOIN (
   SELECT article, MAX(price) AS price
   FROM shop
   GROUP BY article) AS s2
   ON s1.article = s2.article AND s1.price = s2.price
 ORDER BY article;

'LEFT JOIN':

 SELECT s1.article, s1.dealer, s1.price
 FROM shop s1
 LEFT JOIN shop s2 ON s1.article = s2.article AND s1.price < s2.price
 WHERE s2.article IS NULL
 ORDER BY s1.article;

The 'LEFT JOIN' works on the basis that when 's1.price' is at its maximum value, there is no 's2.price' with a greater value and thus the corresponding 's2.article' value is 'NULL'. See *note join::.

 File: manual.info.tmp, Node: example-user-variables, Next: example-foreign-keys, Prev: example-maximum-column-group-row, Up: examples

3.6.5 Using User-Defined Variables

You can employ MySQL user variables to remember results without having to store them in temporary variables in the client. (See *note user-variables::.)

For example, to find the articles with the highest and lowest price you can do this:

 mysql> SELECT @min_price:=MIN(price),@max_price:=MAX(price) FROM shop;
 mysql> SELECT * FROM shop WHERE price=@min_price OR price=@max_price;
 +---------+--------+-------+
 | article | dealer | price |
 +---------+--------+-------+
 |    0003 | D      |  1.25 |
 |    0004 | D      | 19.95 |
 +---------+--------+-------+

Note:

It is also possible to store the name of a database object such as a table or a column in a user variable and then to use this variable in an SQL statement; however, this requires the use of a prepared statement. See *note sql-prepared-statements::, for more information.

 File: manual.info.tmp, Node: example-foreign-keys, Next: searching-on-two-keys, Prev: example-user-variables, Up: examples

3.6.6 Using Foreign Keys

MySQL supports foreign keys, which permit cross-referencing related data across tables, and foreign key constraints, which help keep the related data consistent.

A foreign key relationship involves a parent table that holds the initial column values, and a child table with column values that reference the parent column values. A foreign key constraint is defined on the child table.

This following example relates 'parent' and 'child' tables through a single-column foreign key and shows how a foreign key constraint enforces referential integrity.

Create the parent and child tables using the following SQL statements:

 CREATE TABLE parent (
     id INT NOT NULL,
     PRIMARY KEY (id)
 ) ENGINE=INNODB;

 CREATE TABLE child (
     id INT,
     parent_id INT,
     INDEX par_ind (parent_id),
     FOREIGN KEY (parent_id)
         REFERENCES parent(id)
 ) ENGINE=INNODB;

Insert a row into the parent table, like this:

 mysql> INSERT INTO parent (id) VALUES (1);

Verify that the data was inserted. You can do this simply by selecting all rows from 'parent', as shown here:

 mysql> SELECT * FROM parent;
 +----+
 | id |
 +----+
 |  1 |
 +----+

Insert a row into the child table using the following SQL statement:

 mysql> INSERT INTO child (id,parent_id) VALUES (1,1);

The insert operation is successful because 'parent_id' 1 is present in the parent table.

Insertion of a row into the child table with a 'parent_id' value that is not present in the parent table is rejected with an error, as you can see here:

 mysql> INSERT INTO child (id,parent_id) VALUES(2,2);
 ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails
 (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`)
 REFERENCES `parent` (`id`))

The operation fails because the specified 'parent_id' value does not exist in the parent table.

Trying to delete the previously inserted row from the parent table also fails, as shown here:

 mysql> DELETE FROM parent WHERE id = 1;
 ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails
 (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`)
 REFERENCES `parent` (`id`))

This operation fails because the record in the child table contains the referenced id ('parent_id') value.

When an operation affects a key value in the parent table that has matching rows in the child table, the result depends on the referential action specified by 'ON UPDATE' and 'ON DELETE' subclauses of the 'FOREIGN KEY' clause. Omitting 'ON DELETE' and 'ON UPDATE' clauses (as in the current child table definition) is the same as specifying the 'RESTRICT' option, which rejects operations that affect a key value in the parent table that has matching rows in the parent table.

To demonstrate 'ON DELETE' and 'ON UPDATE' referential actions, drop the child table and recreate it to include 'ON UPDATE' and 'ON DELETE' subclauses with the 'CASCADE' option. The 'CASCADE' option automatically deletes or updates matching rows in the child table when deleting or updating rows in the parent table.

 DROP TABLE child;

 CREATE TABLE child (
     id INT,
     parent_id INT,
     INDEX par_ind (parent_id),
     FOREIGN KEY (parent_id)
         REFERENCES parent(id)
         ON UPDATE CASCADE
         ON DELETE CASCADE
 ) ENGINE=INNODB;

Insert some rows into the child table using the statement shown here:

 mysql> INSERT INTO child (id,parent_id) VALUES(1,1),(2,1),(3,1);

Verify that the data was inserted, like this:

 mysql> SELECT * FROM child;
 +------+-----------+
 | id   | parent_id |
 +------+-----------+
 |    1 |         1 |
 |    2 |         1 |
 |    3 |         1 |
 +------+-----------+

Update the ID in the parent table, changing it from 1 to 2, using the SQL statement shown here:

 mysql> UPDATE parent SET id = 2 WHERE id = 1;

Verify that the update was successful by selecting all rows from the parent table, as shown here:

 mysql> SELECT * FROM parent;
 +----+
 | id |
 +----+
 |  2 |
 +----+

Verify that the 'ON UPDATE CASCADE' referential action updated the child table, like this:

 mysql> SELECT * FROM child;
 +------+-----------+
 | id   | parent_id |
 +------+-----------+
 |    1 |         2 |
 |    2 |         2 |
 |    3 |         2 |
 +------+-----------+

To demonstrate the 'ON DELETE CASCADE' referential action, delete records from the parent table where 'parent_id = 2'; this deletes all records in the parent table.

 mysql> DELETE FROM parent WHERE id = 2;

Because all records in the child table are associated with 'parent_id = 2', the 'ON DELETE CASCADE' referential action removes all records from the child table, as shown here:

 mysql> SELECT * FROM child;
 Empty set (0.00 sec)

For more information about foreign key constraints, see *note create-table-foreign-keys::.

 File: manual.info.tmp, Node: searching-on-two-keys, Next: calculating-days, Prev: example-foreign-keys, Up: examples

3.6.7 Searching on Two Keys

An 'OR' using a single key is well optimized, as is the handling of 'AND'.

The one tricky case is that of searching on two different keys combined with 'OR':

 SELECT field1_index, field2_index FROM test_table
 WHERE field1_index = '1' OR  field2_index = '1'

This case is optimized. See *note index-merge-optimization::.

You can also solve the problem efficiently by using a note 'UNION': union. that combines the output of two separate note 'SELECT': select. statements. See *note union::.

Each *note 'SELECT': select. searches only one key and can be optimized:

 SELECT field1_index, field2_index
     FROM test_table WHERE field1_index = '1'
 UNION
 SELECT field1_index, field2_index
     FROM test_table WHERE field2_index = '1';

 File: manual.info.tmp, Node: calculating-days, Next: example-auto-increment, Prev: searching-on-two-keys, Up: examples

3.6.8 Calculating Visits Per Day

The following example shows how you can use the bit group functions to calculate the number of days per month a user has visited a Web page.

 CREATE TABLE t1 (year YEAR, month INT UNSIGNED,
              day INT UNSIGNED);
 INSERT INTO t1 VALUES(2000,1,1),(2000,1,20),(2000,1,30),(2000,2,2),
             (2000,2,23),(2000,2,23);

The example table contains year-month-day values representing visits by users to the page. To determine how many different days in each month these visits occur, use this query:

 SELECT year,month,BIT_COUNT(BIT_OR(1<<day)) AS days FROM t1
        GROUP BY year,month;

Which returns:

 +------+-------+------+
 | year | month | days |
 +------+-------+------+
 | 2000 |     1 |    3 |
 | 2000 |     2 |    2 |
 +------+-------+------+

The query calculates how many different days appear in the table for each year/month combination, with automatic removal of duplicate entries.

 File: manual.info.tmp, Node: example-auto-increment, Prev: calculating-days, Up: examples

3.6.9 Using AUTO_INCREMENT

The 'AUTO_INCREMENT' attribute can be used to generate a unique identity for new rows:

 CREATE TABLE animals (
      id MEDIUMINT NOT NULL AUTO_INCREMENT,
      name CHAR(30) NOT NULL,
      PRIMARY KEY (id)
 );

 INSERT INTO animals (name) VALUES
     ('dog'),('cat'),('penguin'),
     ('lax'),('whale'),('ostrich');

 SELECT * FROM animals;

Which returns:

 +----+---------+
 | id | name    |
 +----+---------+
 |  1 | dog     |
 |  2 | cat     |
 |  3 | penguin |
 |  4 | lax     |
 |  5 | whale   |
 |  6 | ostrich |
 +----+---------+

No value was specified for the 'AUTO_INCREMENT' column, so MySQL assigned sequence numbers automatically. You can also explicitly assign 0 to the column to generate sequence numbers, unless the 'NO_AUTO_VALUE_ON_ZERO' SQL mode is enabled. For example:

 INSERT INTO animals (id,name) VALUES(0,'groundhog');

If the column is declared 'NOT NULL', it is also possible to assign 'NULL' to the column to generate sequence numbers. For example:

 INSERT INTO animals (id,name) VALUES(NULL,'squirrel');

When you insert any other value into an 'AUTO_INCREMENT' column, the column is set to that value and the sequence is reset so that the next automatically generated value follows sequentially from the largest column value. For example:

 INSERT INTO animals (id,name) VALUES(100,'rabbit');
 INSERT INTO animals (id,name) VALUES(NULL,'mouse');
 SELECT * FROM animals;
 +-----+-----------+
 | id  | name      |
 +-----+-----------+
 |   1 | dog       |
 |   2 | cat       |
 |   3 | penguin   |
 |   4 | lax       |
 |   5 | whale     |
 |   6 | ostrich   |
 |   7 | groundhog |
 |   8 | squirrel  |
 | 100 | rabbit    |
 | 101 | mouse     |
 +-----+-----------+

Updating an existing 'AUTO_INCREMENT' column value in an 'InnoDB' table does not reset the 'AUTO_INCREMENT' sequence as it does for 'MyISAM' and 'NDB' tables.

You can retrieve the most recent automatically generated 'AUTO_INCREMENT' value with the 'LAST_INSERT_ID()' SQL function or the 'mysql_insert_id()' (https://dev.mysql.com/doc/c-api/5.7/en/mysql-insert-id.html) C API function. These functions are connection-specific, so their return values are not affected by another connection which is also performing inserts.

Use the smallest integer data type for the 'AUTO_INCREMENT' column that is large enough to hold the maximum sequence value you need. When the column reaches the upper limit of the data type, the next attempt to generate a sequence number fails. Use the 'UNSIGNED' attribute if possible to allow a greater range. For example, if you use note 'TINYINT': integer-types, the maximum permissible sequence number is 127. For note 'TINYINT UNSIGNED': integer-types, the maximum is 255. See *note integer-types:: for the ranges of all the integer types.

Note:

For a multiple-row insert, 'LAST_INSERT_ID()' and 'mysql_insert_id()' (https://dev.mysql.com/doc/c-api/5.7/en/mysql-insert-id.html) actually return the 'AUTO_INCREMENT' key from the first of the inserted rows. This enables multiple-row inserts to be reproduced correctly on other servers in a replication setup.

To start with an 'AUTO_INCREMENT' value other than 1, set that value with note 'CREATE TABLE': create-table. or note 'ALTER TABLE': alter-table, like this:

 mysql> ALTER TABLE tbl AUTO_INCREMENT = 100;

InnoDB Notes

For information about 'AUTO_INCREMENT' usage specific to 'InnoDB', see *note innodb-auto-increment-handling::.

MyISAM Notes

Further Reading

More information about 'AUTO_INCREMENT' is available here:

 File: manual.info.tmp, Node: apache, Prev: examples, Up: tutorial