SQLを使うなら理解しておきたいアルゴリズム?(というか、select文の書き方) - where・order by・join・group by

SQLのselect文の書き方の覚書です。
なお、本文中の動作確認は「mysql Ver 5.0.67」で行いました。

2009/10/05 13:30 追記

予想外に多くのアクセスとブクマをいただき、正直驚いております。
本文はsqlの内部処理には一切触れておりません。ごく普通のsqlの書き方にのみ触れています。
釣りのつもりはありませんが、釣られたと感じた方にはごめんなさい。

説明に使用するデータ構造(というかテーブル)

select文の使い方を説明するために、以下のようなテーブルを使います。

create table countries (
    name nvarchar(30),
    currency char(3),
    population int
);

insert into countries values ( "日本",             "JPY", 127156000 );
insert into countries values ( "フランス",         "EUR", 65073482 );
insert into countries values ( "スペイン",         "EUR", 44904000 );
insert into countries values ( "ロシア",           "RUB", 141903979 );
insert into countries values ( "ベトナム",         "VND", 84238000 );
insert into countries values ( "カンボジア",       "KHR", 14805000 );
insert into countries values ( "コートジボワール", "XOF", 44904000 );

このテーブルは、以下のselect文で表示できます。

select * from countries;

このselect文の実行結果は以下の通りです。

+--------------------------+----------+------------+
| name                     | currency | population |
+--------------------------+----------+------------+
| 日本                     | JPY      |  127156000 |
| フランス                 | EUR      |   65073482 |
| スペイン                 | EUR      |   44904000 |
| ロシア                   | RUB      |  141903979 |
| ベトナム                 | VND      |   84238000 |
| カンボジア               | KHR      |   14805000 |
| コートジボワール         | XOF      |   44904000 |
+--------------------------+----------+------------+
7 rows in set (0.00 sec)

■抽出(where句)

select文のwhere句を使うと、テーブルの中から特定の条件を満たすデータを抽出できます。

例として、商品テーブルproductsから特定の条件を満たすデータを抽出します。

create table products (
    product_id char(4),
    product_name char(30),
    price int
);

insert into products values( "1010", "えんぴつ",   80 );
insert into products values( "1020", "ボールペン", 100 );
insert into products values( "1030", "消しゴム",   100 );
insert into products values( "2010", "定規",       140 );
insert into products values( "2020", "コンパス",   300 );
insert into products values( "3010", "のり",       200 );

商品テーブルproductsを表示すると、以下のようになります。

+------------+-----------------+-------+
| product_id | product_name    | price |
+------------+-----------------+-------+
| 1010       | えんぴつ        |    80 |
| 1020       | ボールペン      |   100 |
| 1030       | 消しゴム        |   100 |
| 2010       | 定規            |   140 |
| 2020       | コンパス        |   300 |
| 3010       | のり            |   200 |
+------------+-----------------+-------+
6 rows in set (0.00 sec)

以下のselect文は、商品テーブルproductsから「price > 100」を満たすデータを抽出します。

select * from products where price > 100;

抽出結果は以下のようになります。

+------------+--------------+-------+
| product_id | product_name | price |
+------------+--------------+-------+
| 2010       | 定規         |   140 |
| 2020       | コンパス     |   300 |
| 3010       | のり         |   200 |
+------------+--------------+-------+
3 rows in set (0.00 sec)

■ソート(order by句)

select文のorder by句を使うと、テーブルを特定の条件でソートできます。

ソートの例(昇順)

例として、売上テーブルsalesを特定の項目の「昇順」にソートします。

create table sales (
    sales_date char(8),
    sales_no char(5),
    product_id char(4),
    amount int
);

insert into sales values( "20090702", "00001", "1020", 10 );
insert into sales values( "20090702", "00002", "1010", 5 );
insert into sales values( "20090702", "00003", "3010", 20 );
insert into sales values( "20090703", "00001", "1030", 10 );
insert into sales values( "20090703", "00002", "1020", 15 );
insert into sales values( "20090704", "00001", "2020", 30 );
insert into sales values( "20090704", "00002", "3010", 20 );

売上テーブルsalesを表示すると、以下のようになります。

+------------+----------+------------+--------+
| sales_date | sales_no | product_id | amount |
+------------+----------+------------+--------+
| 20090702   | 00001    | 1020       |     10 |
| 20090702   | 00002    | 1010       |      5 |
| 20090702   | 00003    | 3010       |     20 |
| 20090703   | 00001    | 1030       |     10 |
| 20090703   | 00002    | 1020       |     15 |
| 20090704   | 00001    | 2020       |     30 |
| 20090704   | 00002    | 3010       |     20 |
+------------+----------+------------+--------+
7 rows in set (0.00 sec)


以下のselect文は、売上テーブルsalesを「sales_noの昇順」にソートします。

select * from sales order by sales_no;

ソート結果は以下のようになります。

+------------+----------+------------+--------+
| sales_date | sales_no | product_id | amount |
+------------+----------+------------+--------+
| 20090702   | 00001    | 1020       |     10 |
| 20090703   | 00001    | 1030       |     10 |
| 20090704   | 00001    | 2020       |     30 |
| 20090702   | 00002    | 1010       |      5 |
| 20090703   | 00002    | 1020       |     15 |
| 20090704   | 00002    | 3010       |     20 |
| 20090702   | 00003    | 3010       |     20 |
+------------+----------+------------+--------+
7 rows in set (0.00 sec)
ソートの例(降順)

例をもう一つ挙げます。商品テーブルproductsを特定の項目の「降順」にソートします。

create table products (
    product_id char(4),
    product_name char(30),
    price int
);

insert into products values( "2010", "定規",       140 );
insert into products values( "2020", "コンパス",   300 );
insert into products values( "3010", "のり",       200 );
insert into products values( "1030", "消しゴム",   100 );
insert into products values( "1020", "ボールペン", 100 );
insert into products values( "1010", "えんぴつ",   80 );

商品テーブルproductsを表示すると、以下のようになります。

+------------+-----------------+-------+
| product_id | product_name    | price |
+------------+-----------------+-------+
| 2010       | 定規            |   140 |
| 2020       | コンパス        |   300 |
| 3010       | のり            |   200 |
| 1030       | 消しゴム        |   100 |
| 1020       | ボールペン      |   100 |
| 1010       | えんぴつ        |    80 |
+------------+-----------------+-------+
6 rows in set (0.00 sec)


以下のselect文は、商品データproductsを「product_idの降順」にソートします。

select * from products order by product_id desc;

ソート結果は以下のようになります。

+------------+-----------------+-------+
| product_id | product_name    | price |
+------------+-----------------+-------+
| 3010       | のり            |   200 |
| 2020       | コンパス        |   300 |
| 2010       | 定規            |   140 |
| 1030       | 消しゴム        |   100 |
| 1020       | ボールペン      |   100 |
| 1010       | えんぴつ        |    80 |
+------------+-----------------+-------+
6 rows in set (0.00 sec)

■結合(join句)

select文のjoin句を使うと、2つのテーブルを特定の条件で結合できます。

例として、商品テーブルproductsと売上テーブルsalesを結合します。

create table products (
    product_id char(4),
    product_name char(30),
    price int
);

insert into products values( "1010", "えんぴつ",   80 );
insert into products values( "1020", "ボールペン", 100 );
insert into products values( "1030", "消しゴム",   100 );
insert into products values( "2010", "定規",       140 );
insert into products values( "2020", "コンパス",   300 );
insert into products values( "3010", "のり",       200 );

create table sales (
    sales_date char(8),
    sales_no char(5),
    product_id char(4),
    amount int
);

insert into sales values( "20090702", "00001", "1020", 10 );
insert into sales values( "20090702", "00002", "1010", 5 );
insert into sales values( "20090702", "00003", "3010", 20 );
insert into sales values( "20090703", "00001", "1030", 10 );
insert into sales values( "20090703", "00002", "1020", 15 );
insert into sales values( "20090704", "00001", "2020", 30 );
insert into sales values( "20090704", "00002", "3010", 20 );

商品テーブルproductsを表示すると以下のようになります。

+------------+-----------------+-------+
| product_id | product_name    | price |
+------------+-----------------+-------+
| 1010       | えんぴつ        |    80 |
| 1020       | ボールペン      |   100 |
| 1030       | 消しゴム        |   100 |
| 2010       | 定規            |   140 |
| 2020       | コンパス        |   300 |
| 3010       | のり            |   200 |
+------------+-----------------+-------+
6 rows in set (0.00 sec)

また、売上テーブルsalesを表示すると以下のようになります。

+------------+----------+------------+--------+
| sales_date | sales_no | product_id | amount |
+------------+----------+------------+--------+
| 20090702   | 00001    | 1020       |     10 |
| 20090702   | 00002    | 1010       |      5 |
| 20090702   | 00003    | 3010       |     20 |
| 20090703   | 00001    | 1030       |     10 |
| 20090703   | 00002    | 1020       |     15 |
| 20090704   | 00001    | 2020       |     30 |
| 20090704   | 00002    | 3010       |     20 |
+------------+----------+------------+--------+
7 rows in set (0.00 sec)


以下のselect文は、商品テーブルと売上テーブルをproduct_idをキーとして結合します。

select s.sales_date, s.sales_no, p.product_id, p.product_name, s.amount, p.price
from products p join sales s on p.product_id = s.product_id;

結合結果は以下のようになります。

+------------+----------+------------+-----------------+--------+-------+
| sales_date | sales_no | product_id | product_name    | amount | price |
+------------+----------+------------+-----------------+--------+-------+
| 20090702   | 00001    | 1020       | ボールペン      |     10 |   100 |
| 20090702   | 00002    | 1010       | えんぴつ        |      5 |    80 |
| 20090702   | 00003    | 3010       | のり            |     20 |   200 |
| 20090703   | 00001    | 1030       | 消しゴム        |     10 |   100 |
| 20090703   | 00002    | 1020       | ボールペン      |     15 |   100 |
| 20090704   | 00001    | 2020       | コンパス        |     30 |   300 |
| 20090704   | 00002    | 3010       | のり            |     20 |   200 |
+------------+----------+------------+-----------------+--------+-------+
7 rows in set (0.00 sec)

■集計(group by句)

select文のgroup by句を使うと、テーブルを特定の条件で集計できます。

例として、売上データsalesを集計します。

create table sales (
    sales_date char(8),
    sales_no char(5),
    product_name char(30),
    price int,
    amount int
);

insert into sales values( "20090702", "00001", "ボールペン", 100, 10 );
insert into sales values( "20090702", "00002", "えんぴつ",   80,  5 );
insert into sales values( "20090702", "00003", "のり",       200, 20 );
insert into sales values( "20090703", "00001", "消しゴム",   100, 10 );
insert into sales values( "20090703", "00002", "ボールペン", 100, 15 );
insert into sales values( "20090704", "00001", "コンパス",   300, 30 );
insert into sales values( "20090704", "00002", "のり",       200, 20 );

売上テーブルsalesを表示すると以下のようになります。

+------------+----------+-----------------+-------+--------+
| sales_date | sales_no | product_name    | price | amount |
+------------+----------+-----------------+-------+--------+
| 20090702   | 00001    | ボールペン      |   100 |     10 |
| 20090702   | 00002    | えんぴつ        |    80 |      5 |
| 20090702   | 00003    | のり            |   200 |     20 |
| 20090703   | 00001    | 消しゴム        |   100 |     10 |
| 20090703   | 00002    | ボールペン      |   100 |     15 |
| 20090704   | 00001    | コンパス        |   300 |     30 |
| 20090704   | 00002    | のり            |   200 |     20 |
+------------+----------+-----------------+-------+--------+
7 rows in set (0.01 sec)


以下のselect文は、売上テーブルsalesを集計してsales_date毎の合計数量と合計売上高を求めます。

select sales_date, sum(amount), sum(price * amount) from sales group by sales_date;

集計結果は以下のようになります。

+------------+-------------+---------------------+
| sales_date | sum(amount) | sum(price * amount) |
+------------+-------------+---------------------+
| 20090702   |          35 |                5400 |
| 20090703   |          25 |                2500 |
| 20090704   |          50 |               13000 |
+------------+-------------+---------------------+
3 rows in set (0.00 sec)