Home > MySQL Archive

MySQL Archive

MySQL: 同じ値のフィールドをグルーピング - GROUP BY句

前回はソートした検索結果を出力しましたね。今回は、フィールドの値が同じレコードをグルーピングし、そのレコード集の任意のカラムに対してAVG(),MIN(),MAX(),COUNT(),SUM()...などの関数計算を適応してみます。使用するレコードは以下のものを用います。

mysql> SELECT * FROM product_list
    -> ORDER BY date, name;
+----+-----------+----------+------------+
| id | name      | quantity | date       |
+----+-----------+----------+------------+
| 10 | chocolate | 18       | 2009-11-17 |
|  7 | cake      | 35       | 2009-11-18 |
|  6 | candy     | 28       | 2009-11-18 |
|  3 | chocolate | 40       | 2009-11-18 |
|  8 | parfait   | 18       | 2009-11-18 |
|  4 | cake      | 29       | 2009-11-19 |
|  2 | candy     | 32       | 2009-11-19 |
|  1 | chocolate | 16       | 2009-11-19 |
|  5 | parfait   | 29       | 2009-11-19 |
|  9 | eclair    | 56       | 2009-11-20 |
+----+-----------+----------+------------+

背景状況はとあるお菓子工場の11/17~20までの出荷製品とその個数の記録、とでもしておきましょう。さて、このレコード集から「4日間で一番出荷されたお菓子」を判別するクエリは以下のようになります。

mysql> SELECT name, SUM(quantity)
    -> FROM product_list
    -> GROUP BY name
    -> ORDER BY SUM(quantity) DESC;
+-----------+---------------+
| name      | SUM(quantity) |
+-----------+---------------+
| chocolate |            74 |
| cake      |            64 |
| candy     |            60 |
| eclair    |            56 |
| parfait   |            47 |
+-----------+---------------+

<SELECT文の前半(FROM, WHERE句など)>
GROUP BY <列名>

まず、結果の列名がSUM(quantity)になっていることに注目してください。GROUP BY句によりnameフィールドが同じ値のレコードを集計します。例えばnameフィールドがcakeのレコードはidが7と4です。その二つのレコードは一つのグループとみなされます(同じ値なので)。そのグループ(レコード集)に対してSUM()関数を適応しています。SUM()は合計を返しますので、35+29=64と上の出力結果になります。他のフィールド値に対しても同様の計算を行うことで、「4日間で一番出荷されたお菓子」はchocolateと分かります(最後にLIMIT 1を付けてもいいです)。

同様に「1日あたりの平均出荷量が最高のお菓子」を求めるクエリは下のようになります。

mysql> SELECT name, ROUND(AVG(quantity),0)
    -> FROM product_list
    -> GROUP BY name
    -> ORDER BY AVG(quantity) DESC;
+-----------+------------------------+
| name      | ROUND(AVG(quantity),0) |
+-----------+------------------------+
| eclair    |                     56 |
| cake      |                     32 |
| candy     |                     30 |
| chocolate |                     25 |
| parfait   |                     23 |
+-----------+------------------------+

ROUND()関数は小数点の切捨てに使っています。
もしGROUP BY句を用いずに上述の関数(AVG,SUM)を用いると以下のようなエラーが出ます。

mysql> SELECT name, ROUND(AVG(quantity),0)
    -> FROM product_list;
ERROR 1140 (42000): Mixing of GROUP columns (MIN(),MAX(),COUNT(),...)
with no GROUP columns is illegal if there is no GROUP BY clause

グループカラム(=同値のフィールドのレコード集)を渡さなければいけませんので、1つのカラムを渡しても意味無い、という解釈でいいのかな。
ちなみに、COUNT()関数は引数に列(カラム)名を取り、その行(レコード)数を返します

mysql> SELECT COUNT(name)
    -> FROM product_list;
+-------------+
| count(name) |
+-------------+
|          10 |
+-------------+

この場合はGROUP BY句も必要ありません。
今記事では取り上げませんでしたがMIN(),MAX()はそれぞれ最小、最大値を返します(文字どおりですね)。

リファレンス

MySQL: レコードを昇順・降順にソートして出力 - ORDER BY句

前回はUPDATE文とDELETE文を扱いました。今回は下記のレコードを含むテーブルに対して、SELECT文のORDER BY句を用いて任意のレコードをソートして出力する例を示します。

mysql> SELECT * FROM book_list_auto;
+----+------------------------------------+---------+-------+------------+
| id | title                              | author  | price | date       |
+----+------------------------------------+---------+-------+------------+
|  1 | eu,                                | Brody   | 2330  | 2004-06-09 |
|  2 | rutrum. Fusce dolor quam,          | James   | 2150  | 2006-02-25 |
|  3 | ultrices sit amet, risus.          | Wang    | 2495  | 2009-11-12 |
|  4 | placerat, augue. Sed molestie.     | Lev     | 2752  | 2005-05-02 |
|  5 | Proin mi.                          | Dante   | 1155  | 2004-07-23 |
|  6 | sed,                               | Porter  | 4566  | 2006-05-14 |
|  7 | onec sollicitudin adipiscing       | Sader   | 2503  | 2004-04-28 |
|  8 | tortor. Integer aliquam adipiscing | Desiree | 2810  | 2002-08-24 |
|  9 | Mauris eu                          | Maggy   | 3843  | 2001-04-21 |
| 10 | eget metus. In nec orci.           | Rhea    | 4820  | 2002-11-05 |
+----+------------------------------------+---------+-------+------------+

ランダムに生成した10レコード(ブックリスト)を使います。

まず、値段の安い順、すなわちpriceフィールドを昇順で出力するSQL文は下のようになります。

mysql> SELECT title, price
    -> FROM book_list_auto
    -> ORDER BY price;
+------------------------------------+-------+
| title                              | price |
+------------------------------------+-------+
| Proin mi.                          | 1155  |
| rutrum. Fusce dolor quam,          | 2150  |
| eu,                                | 2330  |
| ultrices sit amet, risus.          | 2495  |
| onec sollicitudin adipiscing       | 2503  |
| placerat, augue. Sed molestie.     | 2752  |
| tortor. Integer aliquam adipiscing | 2810  |
| Mauris eu                          | 3843  |
| sed,                               | 4566  |
| eget metus. In nec orci.           | 4820  |
+------------------------------------+-------+

<SELECT文の前半(FROM, WHERE句など)>
ORDER BY <列名1> [, <列名2>, ...]

ORDER BYの後ろにフィールド名を指定することで、そのフィールド基準にソートしたレコードを返します。デフォルトでは昇順(ascending)ソートでそれを明示的に示す場合は、ASCキーワードをフィールド名の後ろに付け足します。上の例では、最終行をORDER BY price ASC;しても結果は同じです。

対して、あるフィールドを降順(descending)にソートする場合はフィールド名の後ろにDESCキーワードを付け足します。以下の例で確認してみましょう。

mysql> SELECT title, price
    -> FROM book_list_auto
    -> ORDER BY price DESC
    -> LIMIT 3;
+--------------------------+-------+
| title                    | price |
+--------------------------+-------+
| eget metus. In nec orci. | 4820  |
| sed,                     | 4566  |
| Mauris eu                | 3843  |
+--------------------------+-------+

確かに、降順、ここで例では値段の高い順にソートされていますね。

LIMIT句で表示行数を指定

さて、ここで使われているLIMIT句は出力する行数を指定することが出来ます。上の例では

LIMIT <表示行数>

より3行(値段の高い本ベスト3)を表示しています。LIMIT句には他にも、

LIMIT <表示開始インデックス>,<表示行数>

という書き方があり、以下のSQL文は上と同じ結果を出力します。ちなみにインデックスは0から数え始めます。

mysql> SELECT title, price
    -> FROM book_list_auto
    -> ORDER BY price DESC
    -> LIMIT 0, 3;

最後に、ORDER BY句では複数のフィールドを指定できますが、その際のソートの適応順序は指定した順(書いた順)になります。次の例で確認してみましょう。

mysql> SELECT title, author, date
    -> FROM book_list_auto
    -> ORDER BY date DESC, author ASC
    -> LIMIT 5;
+--------------------------------+--------+------------+
| title                          | author | date       |
+--------------------------------+--------+------------+
| ultrices sit amet, risus.      | Wang   | 2009-11-12 |
| sed,                           | Porter | 2006-05-14 |
| rutrum. Fusce dolor quam,      | James  | 2006-02-25 |
| placerat, augue. Sed molestie. | Lev    | 2005-05-02 |
| Proin mi.                      | Dante  | 2004-07-23 |
+--------------------------------+--------+------------+

上の例では、dateフィールドでソートして、その中で同じ値があった場合はauthorをソートして順序を決めています。うーん、このレコード集ではちょっと分かり難いですね。

リファレンス

MySQL: レコードの更新と削除 - UPDATE、DELETE文

前回はレコードの検索クエリの作成方法を扱いましたが、今回は既存のレコードのフィールド値を更新するUPDATE文と、レコードを削除するDELETE文の練習をしてみます。

まず、扱うテーブル内のレコードの一覧を下記に示します。これは前回と同じです。

mysql> SELECT * FROM book_list;
+---------+--------+--------+-------+-----------------------+
| book_id | title  | author | price | comments              |
+---------+--------+--------+-------+-----------------------+
|       1 | book_A | auth_A |  1500 | good, bad, excellence |
|       2 | book_B | auth_B |  2900 | not bad, good         |
|       3 | book_C | auth_C |  1800 | interesting, amazing  |
|       4 | book_D | auth_D |   700 | sad, depress          |
|       5 | book_E | auth_E |  1200 | good, funny           |
|       6 | book_F | auth_C |  3500 | bored, difficult      |
|       7 | book_G | auth_A |   400 | very good!, excellent |
+---------+--------+--------+-------+-----------------------+

UPDATE文でレコードの更新

それでは試しに、「値段が1000円を超え2000円未満の本を100円値下げする」SQL文を書いてみます。最初は念のため修正するレコードをSELECT文で出力してみます。

mysql> SELECT * FROM book_list
    -> WHERE price > 1000
    -> AND price < 2000;
+---------+--------+--------+-------+-----------------------+
| book_id | title  | author | price | comments              |
+---------+--------+--------+-------+-----------------------+
|       1 | book_A | auth_A |  1500 | good, bad, excellence |
|       3 | book_C | auth_C |  1800 | interesting, amazing  |
|       5 | book_E | auth_E |  1200 | good, funny           |
+---------+--------+--------+-------+-----------------------+

次に、UPDATE文で上の本を100円値下げしましょう。

mysql> UPDATE book_list
    -> SET price = price - 100
    -> WHERE price > 1000
    -> AND price < 2000;
Query OK, 3 rows affected (0.00 sec)
Rows matched: 3  Changed: 3  Warnings: 0

上手くいったようですね。UPDATE文の一般構文は以下のようになります。

UPDATE <テーブル名>
SET <更新する列名1> = <代入する値1> [, <更新列名2> = <代入値2>, ...]
WHERE <条件式>;

WHERE句は省略可能です。SET句内のイコール「=」は代入演算子です(WHERE句の「=」は等価演算子)。複数のフィールドを書き換える際はSET句内の個々の代入式をカンマ「,」で区切ります。UPDATE文は条件に合致する全てのレコードをSET句で書き換えます。実際に書き変わったか確認してみましょう。

mysql> SELECT title, price FROM book_list
    -> WHERE price + 100 > 1000
    -> AND price + 100 < 2000;
+--------+-------+
| title  | price |
+--------+-------+
| book_A |  1400 |
| book_C |  1700 |
| book_E |  1100 |
+--------+-------+

確かに、100円値下げしてありますね。

DELETE文でレコードを削除

一般構文は、

DELETE FROM <テーブル名>
WHERE <条件式>;

です。条件に合致する全てのレコードを削除します。

mysql> DELETE FROM book_list
    -> WHERE title = 'book_G';
Query OK, 1 row affected (0.00 sec)

mysql> SELECT title FROM book_list;
+--------+
| title  |
+--------+
| book_A |
| book_B |
| book_C |
| book_D |
| book_E |
| book_F |
+--------+

確かに、book_Gが削除されていますね。

リファレンス

MySQL: データ検索クエリの基本 - SELECT文、WHERE句、LIKE、IN、BETWEENキーワード

前回までにテーブルに一定のデータを追加しましたので、今回はそのデータを検索するクエリ文を以下の場合に分けて練習してみます。

  • SELECT文の基本形 - WHERE句
  • 比較文字列内にワイルドカードを指定 - LIKEキーワード、「%」、「_」記号
  • 比較範囲の指定 - BETWEENキーワード
  • 複数のOR結合をIN句でまとめる
  • NOTキーワードで条件の反転
  • NULLフィールドの検索 - IS NULL、IS NOT NULL

まず、扱うテーブル内のレコードの一覧を下記に示します。

mysql> SELECT * FROM book_list;
+---------+--------+--------+-------+-----------------------+
| book_id | title  | author | price | comments              |
+---------+--------+--------+-------+-----------------------+
|       1 | book_A | auth_A |  1500 | good, bad, excellence |
|       2 | book_B | auth_B |  2900 | not bad, good         |
|       3 | book_C | auth_C |  1800 | interesting, amazing  |
|       4 | book_D | auth_D |   700 | sad, depress          |
|       5 | book_E | auth_E |  1200 | good, funny           |
|       6 | book_F | auth_C |  3500 | bored, difficult      |
|       7 | book_G | auth_A |   400 | very good!, excellent |
+---------+--------+--------+-------+-----------------------+

SELECT文の基本形 - WHERE句

SELECT文では指定したテーブル(FROM句)に対して、検索条件(WHERE句)にマッチするレコード(行)の指定フィールド(列)を表示します。SELECTに続くのは列名、と覚えましょう。以下、具体例。

SELECT <列名1>[, <列名2>, ...] FROM <テーブル名>
WHERE
<検索条件>;

WHERE句は省略可能。

mysql> SELECT title, price FROM book_list
    -> WHERE
    -> author = 'auth_A';
+--------+-------+
| title  | price |
+--------+-------+
| book_A |  1500 |
| book_G |   400 |
+--------+-------+

author列がauth_Aに合致するレコードを検索し、そのtitle、price列を表示しています。「=」は等価演算子です(代入演算子ではありません)。ちなみに非等価演算子は「<>」です。

mysql> SELECT * FROM book_list
    -> WHERE
    -> price < 1500;
+---------+--------+--------+-------+-----------------------+
| book_id | title  | author | price | comments              |
+---------+--------+--------+-------+-----------------------+
|       4 | book_D | auth_D |   700 | sad, depress          |
|       5 | book_E | auth_E |  1200 | good, funny           |
|       7 | book_G | auth_A |   400 | very good!, excellent |
+---------+--------+--------+-------+-----------------------+

アスタリスク「*」はワイルドカードで全ての列を返します。

mysql> SELECT title, price FROM book_list
    -> WHERE
    -> price >= 1800
    -> AND
    -> author = 'auth_C';
+--------+-------+
| title  | price |
+--------+-------+
| book_C |  1800 |
| book_F |  3500 |
+--------+-------+

WHERE句内の複数の条件を結合する際はAND、ORキーワードを用います。

比較文字列内にワイルドカードを指定 - LIKEキーワード、「%」、「_」記号

WHERE句内の比較条件に用いる演算子の一種と捉えてもいいかもしれません。

mysql> SELECT title, author, comments FROM book_list
    -> WHERE
    -> comments LIKE '%excellen%';
+--------+--------+-----------------------+
| title  | author | comments              |
+--------+--------+-----------------------+
| book_A | auth_A | good, bad, excellence |
| book_G | auth_A | very good!, excellent |
+--------+--------+-----------------------+

「%」は任意の文字列にマッチします。対して「_」任意の1文字にマッチします。

比較範囲の指定 - BETWEENキーワード

price >= 1000 AND price <= 2000の様な条件を簡潔に書くことが出来ます。

mysql> SELECT title, price FROM book_list
    -> WHERE
    -> price >= 1000 AND price <= 2000;
+--------+-------+
| title  | price |
+--------+-------+
| book_A |  1500 |
| book_C |  1800 |
| book_E |  1200 |
+--------+-------+

これを書き直すと、

mysql> SELECT title, price FROM book_list
    -> WHERE
    -> price BETWEEN 1000 AND 2000;
+--------+-------+
| title  | price |
+--------+-------+
| book_A |  1500 |
| book_C |  1800 |
| book_E |  1200 |
+--------+-------+

確かに実行結果は合致していますね。

複数のOR結合をIN句でまとめる

mysql> SELECT title, author, price FROM book_list
    -> WHERE
    -> author IN ('auth_A', 'auth_C');
+--------+--------+-------+
| title  | author | price |
+--------+--------+-------+
| book_A | auth_A |  1500 |
| book_C | auth_C |  1800 |
| book_F | auth_C |  3500 |
| book_G | auth_A |   400 |
+--------+--------+-------+

NOTキーワードで条件の反転

LIKE、BETWEEN、IN句と共にも使用することが出来ます。その場合は通常比較フィールドの直前に書きます。

mysql> SELECT title, author, price FROM book_list
    -> WHERE
    -> NOT author IN ('auth_A', 'auth_C');
+--------+--------+-------+
| title  | author | price |
+--------+--------+-------+
| book_B | auth_B |  2900 |
| book_D | auth_D |   700 |
| book_E | auth_E |  1200 |
+--------+--------+-------+

だだし、INキーワードではINの直前に書いてもOKです。

mysql> SELECT title, author, price FROM book_list
    -> WHERE
    -> author NOT IN ('auth_A', 'auth_C');
+--------+--------+-------+
| title  | author | price |
+--------+--------+-------+
| book_B | auth_B |  2900 |
| book_D | auth_D |   700 |
| book_E | auth_E |  1200 |
+--------+--------+-------+

NULLフィールドの検索 - IS NULL、IS NOT NULL

mysql> SELECT * FROM book_list
    -> WHERE title IS NULL;
Empty set (0.00 sec)
mysql> SELECT * FROM book_list
    -> WHERE title IS NOT NULL;
+---------+--------+--------+-------+-----------------------+
| book_id | title  | author | price | comments              |
+---------+--------+--------+-------+-----------------------+
|       1 | book_A | auth_A |  1500 | good, bad, excellence |
|       2 | book_B | auth_B |  2900 | not bad, good         |
|       3 | book_C | auth_C |  1800 | interesting, amazing  |
|       4 | book_D | auth_D |   700 | sad, depress          |
|       5 | book_E | auth_E |  1200 | good, funny           |
|       6 | book_F | auth_C |  3500 | bored, difficult      |
|       7 | book_G | auth_A |   400 | very good!, excellent |
+---------+--------+--------+-------+-----------------------+

リファレンス

MySQL: データをテーブルに追加 - INSERT文、INTO、VALUES句

INSERT文を用いて1レコードを追加する練習をしてみます。
前回までにテーブルの作成と構造の修正を行いましたが、これから使うテーブルは下記のものにします(少しフィールドを削りました)。

mysql> DESC book_list;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| book_id  | int(11)      | NO   | PRI | NULL    | auto_increment |
| title    | varchar(128) | NO   |     | NULL    |                |
| author   | varchar(32)  | NO   |     | NULL    |                |
| price    | int(11)      | NO   |     | NULL    |                |
| comments | text         | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

テーブルへのデータの挿入は基本的には以下の構文を使います。

INSERT INTO <テーブル名>
(<列名1> , <列名2>, ...)
VALUES
(<列名1へのデータ>, <列名2へのデータ>, ...);

実際にプロンプト上で打つと、

mysql> INSERT INTO book_list
    -> (book_id, title, author, price, comments)
    -> VALUES
    -> (1, 'book_A', 'auth_A', 1500, 'good, bad, excellence');

となります。最初の丸括弧「()」内で列名を列挙し、二つ目のVALUES句の丸括弧内で、挿入するレコード値を書きます。数値はそのまま書きますが、文字列はシングルクォート「'」で囲まなければいけません。仮に文字としてシングルクォート「'」を使いたい場合はその直前に「\」をつける必要があります。すなわち、「\'」と書きます。
また、主キーのフィールドに重複するデータを追加しようとすると下記のようなエラーがでます。

ERROR 1062 (23000): Duplicate entry '1' for key 1

ちなみに、INSERT文にはいくつか下記のような簡略化した書き方があります。

(<列名1> , <列名2>, ...)句の省略

ただし、VALUES (...)句内のデータの順序はテーブルの列の順序に合わせなければいけません。

INSERT INTO <テーブル名>
VALUES
(<列名1へのデータ>, <列名2へのデータ>, ...);

今回のテーブルに使ってみると、

mysql> INSERT INTO book_list
    -> VALUES
    -> (2, 'book_B', 'auth_B', 2900, 'not bad, good');

これは手っ取り早くていいですね。

(<列名Y> , <列名X>, ...)句とVALUES (<列名Yへのデータ>, <列名Xへのデータ>, ...)句内の列順序の変更

二つの丸括弧内の「列名―代入するデータ」の対応が取れていれば、どんな順序で書いても良いです。
仮にテーブル内のフィールドがA, B, Cの順で並んでいても、

INSERT INTO <テーブル名>
(<列名C> , <列名A>, <列名B>, ...)
VALUES
(<列名Cへのデータ> , <列名Aへのデータ>, <列名Bへのデータ>, ...);

のように記述できます。試しに打ってみると、

mysql> INSERT INTO book_list
    -> (author, title, book_id, comments, price)
    -> VALUES
    -> ('auth_C', 'book_C', 3, 'interesting, amazing', 1800);

一部のフィールド値の挿入を省略

二つの丸括弧内の「列名―代入するデータ」の対応が取れていれば、全列へのデータの代入をしなくてもOKです。
例えば、テーブルにA, B, CというフィールドがあったときA, Cだけにデータを追加するには、

INSERT INTO <テーブル名>
(<列名A>, <列名C> , ...)
VALUES
(<列名A>, <列名C>, ...);

と書けます。このとき列Bには、デフォルト値が設定されているならばその値が、ないならばNULLが代入されます。
今回のテーブルでは、

mysql> INSERT INTO book_list
    -> (title, author, price, comments)
    -> VALUES
    -> ('book_D', 'auth_D', 700, 'sad, depress');

book_idフィールドを省略しましたが、このフィールドはauto_incrementを指定してあるので、前に挿入されたbook_idフィールドの値を1増やした値が代入されます。

追加されたデータを確認(検索) - SELECT文

それでは最後に以下のコマンドを打ち、上述で追加されたレコードを確認してみましょう。

mysql> SELECT * FROM book_list;
+---------+--------+--------+-------+-----------------------+
| book_id | title  | author | price | comments              |
+---------+--------+--------+-------+-----------------------+
|       1 | book_A | auth_A |  1500 | good, bad, excellence |
|       2 | book_B | auth_B |  2900 | not bad, good         |
|       3 | book_C | auth_C |  1800 | interesting, amazing  |
|       4 | book_D | auth_D |   700 | sad, depress          |
+---------+--------+--------+-------+-----------------------+

確かにコマンドどおりのレコードが入っていますね。

リファレンス

Page 1 of 212

Home > MySQL Archive

バックナンバー
最近のコメント
最近のトラックバック
メタ情報

Return to page top