独り言

プログラミングの講師をしています。新人研修で扱う技術の解説と個人の技術メモ、技術書の紹介など

【SQL】SQL入門(DML編)

ここではSQLの中でも最も使用頻度の高いDMLについて解説します。

ここからは先のSQL(導入・DBMS概要・DDL)の記事で作成した、ユーザー「testuser」、データベース「testdb」に接続している状態と仮定して学習を進めます。
DDLの個所で作成した「items」テーブルを使用していきます。
前提知識・前提条件については下記の記事を参照ください。

case10.hateblo.jp

コマンドプロンプト(あるいはターミナルなど)からの接続は以下のコマンドで接続できます。

psql -U testuser -d testdb

DMLはData Manipulation Languageの略です。
DDLは、データベースのオブジェクトに対する操作の構文でした。
DMLは、レコード(テーブルのデータ)に対する操作の構文です。
DDL, DML, DCLの3つの中で、最も使用頻度が高いのがDMLです。
DMLには大きく4つの構文があります。

  • INSERT:データの挿入
  • SELECT:データの取得
  • UPDATE:データの更新
  • DELETE:データの削除

この4つの中で最も使用頻度が高いのはSELECT文です。
そして、SELECT文を使いこなすことができれば、他の3つはそれほど難しくはありません。
なのでSELECTに重点をおいて解説していきます。


目次

  • INSERT
  • DELETE
  • UPDATE
  • SELECT
  • GROUP BY
  • 関数
  • JOIN
  • 集合演算

INSERT

INSERT文はテーブルにレコード(データ)を挿入するための構文です。

構文

-- 一般的なインサート文
INSERT INTO テーブル名(カラム1, …) VALUES (値1, …);
-- 全カラムに値をセットするときはカラムのリストは省略可
INSERT INTO テーブル名 VALUES (値1, …);
-- まとめてデータを入れることも可能
-- ただしDBMS製品によってはできない
INSERT INTO テーブル名 VALUES (値1, …), (値1, …), ...;
-- SELECTの実行結果をそのままインサートもできる
INSERT INTO テーブル名 (カラム1, …) SELECT句;

サンプル

-- 一般的なインサート文
INSERT INTO items(id, item_name, price) VALUES(1, 'apple', 100);
-- 全カラムに値をセットするときはカラムのリストは省略可
INSERT INTO items VALUES(2, 'orange', 200);
-- まとめてデータを入れる
INSERT INTO items VALUES(3, 'banana', 300), (4, 'grape', 400);
-- レコードが挿入されていることの確認
SELECT * FROM items;

DELETE

DELETE文はレコードを削除するための構文です。
WHERE句による絞り込み条件についてはSELECT文で学習します。

構文

-- 一般的なデリート文
-- 条件を絞り込まないと全件削除されるので注意
DELETE FROM テーブル名 WHERE 絞り込み条件;

サンプル

-- idが1のレコードを削除する
DELETE from items WHERE id = 1;
-- レコードが削除されていることの確認
SELECT * FROM items;
-- 元に戻しておく
INSERT INTO items(id, item_name, price) VALUES(1, 'apple', 100);

UPDATE

UPDATE文は既存のレコードの値を更新するための構文です。

構文

-- 一般的なUPDATE文
UPDATE テーブル名
  SET カラム1 = 値1
       ,  カラム2 = 値2WHERE 絞り込み条件;

-- Oracleで使用できる構文
UPDATE テーブル名
  SET (カラム1, カラム2, …) = (値1, 値2, …)
       …
WHERE 絞り込み条件;

サンプル

-- idが1のレコードの価格を+100する
UPDATE items
  SET price = price + 100
WHERE id = 1;

-- 更新されたことを確認
SELECT * FROM items;

-- 元に戻す
UPDATE items
  SET price = price - 100
WHERE id = 1;

SELECT

SELECT文はテーブルからレコードを取得するための構文です。
SELECT文で使用できる構文には様々なものがあるので、詳しく紹介していきます。

全カラムを取得

まず、レコードを取得するには、SELECT句で欲しいカラムの情報を書き、その後、FROM句でほしいレコードが格納されているテーブルを指定します。
テーブルの全カラムの情報を取得する場合は「*」を使用します。
構文

-- テーブルの全カラム・全レコードを取得
SELECT * FROM テーブル名; 

サンプル

SELECT * 
FROM items;

結果

id item_name price
1 apple 100
2 orange 200
3 banana 300
4 grape 400

カラムの指定

特定のカラムの情報だけが欲しい場合は、カンマ区切りでカラムを指定します。
構文

-- カラムを指定してレコードを取得
SELECT カラム1, カラム2, .... FROM テーブル名; 

サンプル

-- カラムを指定してレコードを取得
-- SELECT カラム1, カラム2, .... FROM テーブル名; 
SELECT item_name, price 
FROM items;

結果

item_name price
apple 100
orange 200
banana 300
grape 400

レコードの絞り込み

レコードを絞り込みたい場合は、WHERE句を指定します。
構文

-- レコードを絞り込む
SELECT * FROM テーブル名 WHERE 絞り込み条件 

サンプル

-- idが1のレコードを取得
SELECT * 
FROM items 
WHERE id = 1;

結果

id item_name price
1 apple 100

AND

複数の条件を指定して絞り込みたい場合は、ANDを使用します。
サンプル

-- idが1かつitem_nameがapple
SELECT * 
FROM items 
WHERE id = 1
AND item_name = 'apple';

結果

id item_name price
1 apple 100

OR

複数の条件を指定して、いずれかの条件を満たす場合のレコードを対象にしたい場合は、ORを使用します。
サンプル

-- idが1 または item_nameがorange
SELECT * 
FROM items 
WHERE id = 1
OR item_name = 'orange';

結果

id item_name price
1 apple 100
2 orange 200

IN

同じカラムに対して、ORで複数の値を指定したい場合は、INを使うことで簡潔に書くことができます。

-- idが1 または 3
SELECT * 
FROM items 
WHERE id IN (1, 3);
-- WHERE id = 1 OR id = 3; と書いても同じ

結果

id item_name price
1 apple 100
3 banana 300

BETWEEN

指定したカラムに対して、特定の範囲で絞り込みたい場合は、BETWEENを使用します。

-- idが1から3
SELECT * 
FROM items 
WHERE id BETWEEN 1 AND 3;
-- WHERE id >= 1 AND id <= 3;  -- とするのと同じ

結果

id item_name price
1 apple 100
2 orange 200
3 banana 300

LIKE

部分一致による検索、あいまい検索を行いたい場合は、LIKEを使用します。
任意の文字を指定する場合は、「%」を使用します。
任意の1文字を指定する場合は、「_」を使用します。

-- 部分一致
-- 最初がaから始まる
SELECT * FROM items WHERE item_name LIKE 'a%'

結果

id item_name price
1 apple 100
-- 部分一致
-- 最後がaで終わる
SELECT * FROM items WHERE item_name LIKE '%a'

結果

id item_name price
3 banana 300
-- 部分一致
-- item_nameにpが含まれるレコード
SELECT * FROM items WHERE item_name LIKE '%p%'

結果

id item_name price
1 apple 100
4 grape 400

エイリアス

エイリアスは別名という意味です。
エイリアスはカラムやテーブルに対して別名を付けることができる機能です。
カラムにエイリアスを付けることで、結果のカラム名を別名に変更することができます。

-- ASはつけてもつけなくてもよい
SELECT item_name AS 名前, price 値段
FROM items

結果

名前 値段
apple 100
orange 200
banana 300
grape 400

テーブルにエイリアスを付ける方法は、JOIN(結合)の説明の部分で使用します。

演算

SQLでは数値の演算や文字列の結合を行うこともできます。
また、SQLはFROM句がなくても実行することができる(ただしDBMSによってはダミーテーブルの指定が必要な場合もある)ので、合わせて覚えておきましょう。

SELECT 
1  -- 直接値を指定
, 1 + 2  加算 
, 5 - 2  減算
, 3 * 3  乗算
, 10 / 3  除算
, 10 % 3  剰余
, 'abc' || 'def'  文字列結合

結果

1 加算 減算 乗算 除算 剰余 文字列結合
1 3 3 9 3 1 abcdef

比較演算子

今までWHERE句での条件指定では、「=」を使って値が等しい場合のみを扱ってきましたが、それ以外の比較演算子もあります。

SELECT *
FROM items
WHERE id = 1 -- 等しい
AND   id <> 1 -- 等しくない
AND   id != 1 -- 等しくない
AND   price >= 500 -- 以上
AND   price > 500  -- 大きい
AND   price <= 500 -- 以下
AND   price < 500  -- より小さい
AND   item_name IS NULL -- null判定の場合はisを使用する
AND   item_name IS NOT NULL -- 否定の場合はNOTを付ける

NULLに対しては「=」や「<>」での比較ができないので注意してください。

ORDER BY

ORDER BY ではレコードの並び替えを行います。
並び替えの基準としたいカラムを指定し、ASC(昇順)かDESC(降順)かを指定します。
ASCは省略可能です。
また、複数のカラムを指定することも可能です。
その場合、先に書いたカラムの方が優先順位が高くなります。
構文

SELECT句
FROM句
WHERE句
ORDER BY column1 (ASC|DESC), column2 (ASC|DESC),…;

サンプル

SELECT * 
FROM items
ORDER BY item_name ASC; -- 名前の昇順 ASCは省略可

結果

id item_name price
1 apple 100
3 banana 300
4 grape 400
2 orange 200

item_nameの昇順に並びます

SELECT * 
FROM items
ORDER BY price DESC; -- 値段の降順

結果

id item_name price
4 grape 400
3 banana 300
2 orange 200
1 apple 100

priceの降順に並びます

DISTINCT

DISTINCTは取得結果から重複を排除するための構文です。

DISTINCTの結果をみるために以下のSQL文でサンプルデータを作成しておいてください。

create table ex_distinct as 
select '2018/9/1' sales_date, '顧客A' customer_name, '商品①' item_name, 10 quantity, 1000 amount
union
select '2018/9/1', '顧客B', '商品①', 20, 4000
union
select '2018/9/2', '顧客A', '商品③', 10, 3000
union
select '2018/9/2', '顧客B', '商品①', 5, 500
union
select '2018/9/2', '顧客C', '商品③', 5, 1500
union
select '2018/9/3', '顧客C', '商品③', 2, 600
union
select '2018/9/4', '顧客A', '商品①', 5, 500
union
select '2018/9/4', '顧客B', '商品②', 10, 2000
union
select '2018/9/5', '顧客A', '商品②', 3, 600;

構文

SELECT DISTINCT column1, column2, … FROM table1

サンプル

SELECT DISTINCT customer_name, item_name FROM ex_distinct;

結果

customer_name item_name
顧客A 商品①
顧客A 商品②
顧客A 商品③
顧客B 商品①
顧客B 商品②
顧客C 商品③

LIMITとOFFSET

LIMITはレコードの取得件数を制限したい場合に使用します。
合わせてOFFSETを指定すると、読み飛ばす件数も指定することができます。

構文

SELECT句
FROM句
WHERE句
ORDER BY句
LIMIT 取得件数
OFFSET 読み飛ばす件数

サンプル

SELECT * 
FROM items
ORDER BY id
LIMIT 2;  -- 上位に2件を取得

結果

id item_name price
1 apple 100
2 orange 200
SELECT * 
FROM items
ORDER BY id
LIMIT 2  -- 2件を取得
OFFSET 2;  -- 最初の2件を読み飛ばす  

結果

id item_name price
3 banana 300
4 grape 400

GROUP BY

GROUP BY は特定のカラムで集約をすることで、集計したり、最大や最小を特定したりすることができる構文です。
例えば、学生のデータが登録されているテーブルがあった時に、性別ごとの人数を集計したり、誕生月ごとの人数を集計したり、性別ごとのテストの合計点や平均点、最大得点や最小得点などを求めたい場合などに使用します。

GROUP BY の結果を見るために、こちらのSQL文にてサンプルデータを作成しておいてください。

create table ex_group as 
select '2018/9/1' sales_date, '商品A' item_name,  300 price, 10 quantity
union
select '2018/9/2' , '商品A' , 300 , 5 
union
select '2018/9/3' , '商品A' , 300 , 20 
union
select '2018/9/4' , '商品B' , 400 , 8 
union
select '2018/9/5' , '商品B' , 400 , 5    
union
select '2018/9/6' , '商品C' , 500 , 5    
union
select '2018/9/7' , '商品C' , 500 , 10 
union
select '2018/9/8' , '商品C' , 500 , 8 
union
select '2018/9/9' , '商品C' , 500 , 7   
union
select '2018/9/10' , '商品C' , 500 , 10 ;

構文

SELECT column1, SUM(column2), COUNT(*), ... -- 集約関数が使用可能
FROM table1
GROUP BY column1  -- 集約の対象となるカラム
HAVING SUM(column2) > 10000; -- 集約結果を使った絞り込み条件
SELECT 
  item_name -- カラムを直接指定できるのはgroup by で指定したカラムのみ
, COUNT(*) count  -- レコード件数
, SUM(quantity) sum_quantity  -- 数量の合計
, SUM(price * quantity) total   -- 金額の合計
, MAX(quantity) max_quantity  -- 数量の最大値
, MIN(quantity) min_quantity  -- 数量の最小値
, MAX(sales_date) new_sales_date  -- 日付の最大(最新)
FROM ex_group
GROUP BY item_name
ORDER BY item_name;

結果

item_name count sum_quantity total max_quantity min_quantity new_sales_date
商品A 3 35 10500 20 5 2018/9/3
商品B 2 13 5200 8 5 2018/9/5
商品C 5 40 20000 10 5 2018/9/9

軽く解説

GTOUP BY を使用すると、SELECT句の中で直接カラムを指定できるのは、GROUP BY で指定したカラムのみになります。
GROUP BY で指定したカラム以外では、集約関数しか指定できません。
以下、よく使用される集約関数です。

  • COUNT:レコード数を算出
  • SUM:合計値を算出
  • MAX:最大値を算出。数値以外も指定可能
  • MIN:最小値を算出。数値以外も指定可能
  • AVG:平均を算出

COUNT関数の場合、引数には*を指定することが多いですが、カラムを指定することもできます。
カラムを指定した場合、値がNULLの場合はカウント対象にならないので注意してください。
SUMとAVG関数は、引数は数値のカラムしか指定できません。
MINやMAX関数は、数値以外(文字列や日時)も指定可能です。

HAVING

GROUP BY句の後にHAVING句を指定することで、集約関数の結果で絞り込むことができます。

SELECT 
  item_name 
, COUNT(*) count  
, SUM(quantity) sum_quantity  
, SUM(price * quantity) total   
, MAX(quantity) max_quantity  
, MIN(quantity) min_quantity  
, MAX(sales_date) new_sales_date  
FROM ex_group
GROUP BY item_name
HAVING COUNT(*) >= 5 -- レコード件数が5件以上のみ対象
ORDER BY item_name;

結果

item_name count sum_quantity total max_quantity min_quantity new_sales_date
商品C 5 40 20000 10 5 2018/9/9

関数

関数とは、引数で値を受け取り、戻り値で値を返す処理のことです。
一般的なプログラミング言語における関数と同じです。
GROUP BY のところで、SUMやCOUNTなどの関数を使用しました。
それらの関数は、集約関数、集計関数などと呼ばれ、複数のレコードを1つのレコードにまとめる機能を持った関数です。
ここでの関数は、集約関数とは違い、一つの値を別の一つの値に処理するものです。

文字列に関する関数

SELECT 
trim(' abc ') f1             -- 空白を取り除く
, substr('abcdef', 2, 3) f2  -- 部分文字列
, char_length('abcde') f3    -- 文字列の長さ
, lower('HELLO') f4          -- 小文字に変換
, upper('world') f5;         -- 大文字に変換

結果

f1 f2 f3 f4 f5
abc bcd 5 hello WORLD

数値に関する関数

SELECT 
floor(10.8) f1      -- 引数よりも小さい、最大の整数
, ceil(10.8) f2     -- 引数よりも大きい、最大の整数
, random() f3       -- 0~1のランダムな値
, round(10.5) f4    -- 四捨五入
, trunc(10.9) f5;   -- 切り捨て

結果

f1 f2 f3 f4 f5
10 11 0.917... 11 10

ここで使用した関数は、PostgreSQLの環境を前提にした関数です。
DBMSによっては使用できないものや、同じ機能でも名前が異なったりします。
また、ここで紹介した以外にも様々な関数があるので、詳しくはマニュアルなどを参照して調べてみてください。


JOIN(結合)

ここではJOIN(結合)について学習していきます。
結合では、複数のテーブルのデータを結合し、まとめて取得することができます。
結合にはいくつかの種類があります。

  • クロス結合
  • 内部結合
  • 外部結合
    • 左外部結合
    • 右外部結合
    • 全外部結合
  • 自己結合
  • 自然結合

色々ありますがよく使用されるのは内部結合と外部結合です。
一つずつ見ていきます。

クロス結合

クロス結合のサンプルを見るために、こちらのSQL文にてテストデータを作成しておいてください。

create table ex_pg_language as
select 'PHP' pg_language
union
select 'Java'
union
select 'Ruby';

create table ex_dbms as 
select 'Oracle' dbms_name
union
select 'MySQL'
union
select 'PostgreSQL';

クロス結合の書き方は2つあります。
構文

-- 書き方1
SELECT * FROM table1 
CROSS JOIN table2;
-- 書き方2
SELECT * FROM table1, table2

そもそもクロス結合自体、使用する頻度は少ないと思うけれど、2の書き方は分かりにくいので、1の書き方がおすすめ。
サンプル

SELECT *
FROM ex_pg_language
CROSS JOIN ex_dbms; -- クロス結合

結果

pg_language dbms_name
PHP Oracle
PHP MySQL
PHP PostgreSQL
Java Oracle
Java MySQL
Java PostgreSQL
Ruby Oracle
Ruby MySQL
Ruby PostgreSQL

結果から分かる通り、クロス結合はそれぞれのレコードの組み合わせが全て取得される結合です。
レコード数の多いテーブル同士てクロス結合すると、大きな負荷がかかるため、注意が必要です。

内部結合

内部結合のサンプルを見るために、こちらのSQL文にてテストデータを作成しておいてください。

create table ex_items as 
select 1 id , 'りんご' item_name
union
select 2 ,'バナナ'
union
select 3 ,'オレンジ'
union
select 4 ,'ぶどう'
union
select 5 ,'いちご'
union
select 6 ,'メロン';

create table ex_item_price as 
select 1 id, 100 price
union
select 2 ,160
union
select 3 ,80
union
select 7 ,300
union
select 8 ,200
union
select 9 ,600;

内部結合も2つの書き方があります。
構文

-- 書き方1
SELECT * FROM table1 t1
INNER JOIN table2 t2
ON t1.column = t2.column
-- 書き方2
SELECT * FROM table1 t1, table2 t2
WHERE t1.column = t2.column

おすすめの書き方は1の書き方です。
2の書き方の場合、絞り込み条件と結合条件がごっちゃになって分かりにくくなるため、1の書き方がオススメです。
1の書き方の場合、INNERは省略することも可能です。
また、結合の条件が複数ある場合は、ANDで続けて書くこともできます。

サンプル

SELECT item.id, item_name, price
FROM ex_items item  -- テーブル名にエイリアスを付ける
INNER JOIN ex_item_price ex_price
ON item.id = ex_price.id
ORDER BY item.id;

結果

id item_name price
1 りんご 100
2 バナナ 160
3 オレンジ 80

結果から分かる通り、内部結合では、結合対象となるテーブルの両方にデータが存在するレコードのみが取得対象になります。

外部結合

外部結合3つに分かれます。

  • 左外部結合
  • 右外部結合
  • 全外部結合

の3つです。 左外部結合と右外部結合は実質ほぼ同じです。
サンプルデータは内部結合のものと同じものを使用します。

以下はそれぞれの書き方です。
構文

-- 書き方(左外部結合)
SELECT * FROM table1 t1
LEFT OUTER JOIN table2 t2
ON t1.column = t2.column
-- 書き方(右外部結合)
SELECT * FROM table1 t1
RIGHT OUTER JOIN table2 t2
ON t1.column = t2.column
-- 書き方(全外部結合)
SELECT * FROM table1 t1
FULL OUTER JOIN table2 t2
ON t1.column = t2.column

OUTERの部分は省略可能です。
全外部結合についてはDBMSによってはさぽーとされていないものもあるので注意が必要です。

左外部結合

サンプル

-- 左外部結合
SELECT i.id, item_name, price
FROM ex_items i
LEFT OUTER JOIN ex_item_price p
ON i.id = p.id
ORDER BY i.id;

結果

id item_name price
1 りんご 100
2 バナナ 160
3 オレンジ 80
4 ぶどう NULL
5 いちご NULL
6 メロン NULL

左外部結合では、FROMで指定したテーブルのレコードは全て取得されます。
(WHERE句での絞り込みは行われる)
結合対象のテーブルで該当するレコードがない場合は、NULLとして表示されます。

条件指定の書く場所の違い

外部結合では、絞り込みを行うときに、結合条件の時点で絞り込むのか、結合後に条件を絞り込むのかで結果が変わってきます。

-- 結合条件での絞り込み
SELECT i.id, item_name, price
FROM ex_items i
LEFT OUTER JOIN ex_item_price p
ON i.id = p.id
AND p.price >= 100
ORDER BY i.id;

結果

id item_name price
1 りんご 100
2 バナナ 160
4 ぶどう NULL
5 いちご NULL
6 メロン NULL
-- 結合後の条件での絞り込み
SELECT i.id, item_name, price
FROM ex_items i
LEFT OUTER JOIN ex_item_price p
ON i.id = p.id
WHERE p.price >= 100
ORDER BY i.id;

結果

id item_name price
1 りんご 100
2 バナナ 160

それぞれの違いを理解して適切に使いこなせるようにしておきましょう。

右外部結合

右外部結合は、左外部結合の逆です。
本質的には違いはありません。

SELECT p.id, item_name, price
FROM ex_items i
RIGHT OUTER JOIN ex_item_price p
ON i.id = p.id
ORDER BY p.id;

結果

id item_name price
1 りんご 100
2 バナナ 160
3 オレンジ 80
7 NULL 300
8 NULL 200
9 NULL 600

全外部結合

全外部結合は、左外部結合と右外部結合を合わせたものです。
DBMSによってはサポートされていない場合もあるので注意してください。

SELECT i.id id1, p.id id2, item_name, price
FROM ex_items i
FULL OUTER JOIN ex_item_price p
ON i.id = p.id
ORDER BY i.id, p.id;

結果

id item_name price
1 りんご 100
2 バナナ 160
3 オレンジ 80
4 ぶどう NULL
5 いちご NULL
6 メロン NULL
7 NULL 300
8 NULL 200
9 NULL 600

自己結合

自己結合は、結合対象のテーブルが自分自身となっているもので、動作としては内部結合と変わりません。

-- 自己結合
SELECT * FROM table1 t1
INNER JOIN table1 t1
ON t1.column = t1.column

自然結合

自然結合は、結合条件を指定せずに、同じ名前のカラムで自動的に結合してくれる結合方法です。
こちらも、内部結合で表現することも可能なので、内部結合を理解していれば、重要度は下げても構いません。

-- 自然結合
SELECT * FROM table1 
NATURAL JOIN table2
-- 内部結合での表現
SELECT * FROM table1 
INNER JOIN table2
ON table1.id = table2.id

集合演算

集合演算はSELECTの結果の表同士での演算のことです。
3つの種類があります。

  • UNION
  • EXCEPT
  • INTERSECT

UNIONは足し算、EXCEPTは引き算、INTERSECTは掛け算を表します。

集合演算のサンプルを見るために、こちらのSQL文にてテストデータを作成しておいてください。

create table Astore_items as 
select 1 id, 'りんご' item_name, 100 price
union
select 2 , 'バナナ' , 160
union
select 3 , 'オレンジ' , 80
union
select 4 , 'ぶどう' , 300
union
select 5 , 'いちご' , 200;

create table Bstore_items as 
select 1 id, 'りんご' item_name, 100 price
union
select 2 , 'バナナ' , 160
union
select 3 , 'オレンジ' , 90
union
select 6 , 'メロン' , 600;

UNION

UNIONは、レコードの和集合を求める演算子です。
UNIONとUNION ALLが使用できます。

構文

SELECT句
UNION (ALL)
SELECT句

まずはUNIONから

SELECT * FROM Astore_items
UNION
SELECT * FROM Bstore_items;

結果

id item_name price
1 りんご 100
2 バナナ 160
3 オレンジ 80
3 オレンジ 90
4 ぶどう 300
5 いちご 200
6 メロン 600

それぞれのSELECTの結果が合算されて、重複が削除されます。

続いてUNION ALLを見ていきます。

SELECT * FROM Astore_items
UNION ALL
SELECT * FROM Bstore_items;

結果

id item_name price
1 りんご 100
2 バナナ 160
3 オレンジ 80
4 ぶどう 300
5 いちご 200
1 りんご 100
2 バナナ 160
3 オレンジ 90
6 メロン 600

重複しているレコードもそのまま表示されます。
UNIONを私用した場合は、重複レコードは排除されますが、UNION ALLの場合は重複は排除されません。
ここで、並び順にも注意してください。
UNIONは、重複を排除するために、内部的にソートの処理が走ります。
そのため、それぞれの結果が混ざってソートされた形になっていますが、UNION ALLの場合は、重複を排除しないため、ソートがされません。

ソートの処理がない分、UNION ALL の方が処理は高速になります。
それぞれのSELECT文で重複がないことが明確になっており、かつ、並び順を気にしない場合は、UNION ALL を使用したほうが良いでしょう。

EXCEPT

EXCEPTは上の差分を求める演算子です。
書き方はUNIONの場合と同じで、UNIONの部分がEXCEPTに変わるだけです。
DBMSによっては、MINSになっている場合もあります。

SELECT * FROM Astore_items
EXCEPT
SELECT * FROM Bstore_items;

結果

id item_name price
3 オレンジ 80
4 ぶどう 300
5 いちご 200

UNIONと同じく、ALLを指定することもできます。

INTERSECT

INTERSECTは共通部分を求める演算子です。

SELECT * FROM Astore_items
INTERSECT
SELECT * FROM Bstore_items;

結果

id name price
1 りんご 100
2 バナナ 160

サブクエリ

サブクエリは日本語で副問い合わせとも呼ばれます。
SELECT文の結果を他のSQL文に使用するような書き方のことです。
SELECT文の中に別のSELECT文を書くようなイメージです。

サブクエリのサンプルを見るために、こちらのSQL文にてサンプルデータを作成しておいてください。

create table ex_item_sub as 
select '001' item_id, '商品A' item_name, 200 price
union
select '002' , '商品B' , 350
union
select '003' , '商品C' , 170
union
select '004' , '商品D' , 200
union
select '005' , '商品E' , 300;

create table ex_sales_sub as 
select '2018/9/1' sales_date,'001' item_id, 20 quantity
union
select '2018/9/2' ,'001' , 15
union
select '2018/9/3' ,'002' , 40
union
select '2018/9/4' ,'002' , 30
union
select '2018/9/5' ,'003' , 50;

インラインビュー

SELECTの結果を一時的な表とみなして、結合やFROM句で使用するサブクエリです。
サブクエリの結果として使用される一時的な表をインラインビューと呼びます。

-- 例(販売数を商品で集約した結果を、結合に使用する)
SELECT i.*, sum
FROM ex_item_sub i
LEFT OUTER JOIN (
            -- サブクエリ(インラインビューとも呼ぶ)
            SELECT item_id, SUM(quantity) sum 
            FROM ex_sales_sub
            GROUP BY item_id) s
ON i.item_id = s.item_id
ORDER BY i.item_id;

結果

item_id item_name price sum
001 商品A 200 35
002 商品B 350 70
003 商品C 170 50
004 商品D 200 NULL
005 商品E 300 NULL

相関サブクエリ

外側のクエリの値を内側のクエリ内で使用するようなサブクエリのことを相関サブクエリと呼びます。
主にEXISTSを使用する場合などのサブクエリです。
EXISTSは、中のSELECTの結果でレコードが存在する場合にTRUEになるような構文です。

-- 相関サブクエリ
SELECT * FROM ex_item_sub i
WHERE EXISTS (SELECT * FROM ex_sales_sub
            WHERE item_id = i.item_id
            AND sales_date BETWEEN '2018/9/1' AND '2018/9/3');

-- これも上と同じ結果になる
-- 相関サブクエリではない
SELECT * FROM ex_item_sub i
WHERE EXISTS item_id IN (SELECT item_id 
                        FROM ex_sales_sub
                        WHERE sales_date BETWEEN '2018/9/1' AND '2018/9/3')

結果

item_id item_name price
001 商品A 200
002 商品B 350

スカラサブクエリ

SELECT句の中でSELECT文を使用することもできます。
ただしこの場合、SELECTの実行結果は1レコードで、1つの値のみを取得することが可能なSELECT文に限ります。

-- スカラサブクエリ
-- SELECT句の中でSELECT文を使用するサブクエリ
-- 分かりやすいが速度は出にくいのでおすすめはしない
-- 売上のレコードを取得して、商品名も表示する
SELECT 
sales_date
, item_id
, (SELECT item_name FROM ex_item_sub i 
WHERE item_id = s.item_id) item_name
, quantity
FROM ex_sales_sub s;

結果

sales_date item_id item_name quantity
2018/9/4 002 商品B 30
2018/9/1 001 商品A 20
2018/9/3 002 商品B 40
2018/9/2 001 商品A 15
2018/9/5 003 商品C 50

CASE式(条件分岐)

CASEはSQL文の中で条件分岐を行うことができる構文です。
SELECT句、WHERE句、GROUP BY句のなど、どこでも使用可能です。
一般的にはWHERE句では使用せずにSELECT句の中で使用することが多いです。
maxや、sumなどの集約関数の中に入れ込むこともできます。

CASE式のサンプルを見るために、こちらのSQL文にてサンプルデータを作成しておいてください。

create table ex_case as 
select '001' id, '商品A' item_name, 1000 price
union
select '002' , '商品B' , 5000
union
select '003' , '商品C' , 10000;

CASE式の書き方は、単純CASE式と検索CASE式の2つがあります。

-- 単純CASE式
-- 式の値がWHENで一致した値が評価される
SELECT
 CASEWHEN 値A THEN1
    WHEN 値B THEN2
     ELSE3
  END エイリアス名
FROM table2
-- 検索CASE式
-- 条件式がtrueになった時の値が評価される
SELECT
 CASE 
    WHEN 条件式1 THEN1
    WHEN 条件式2 THEN2
     ELSE3
  END エイリアス名
FROM table2
SELECT *
, CASE
      WHEN price >= 10000 THEN '高級品'
      WHEN price BETWEEN 5000 AND 9999 THEN '普通'
      ELSE '安物'
END rank
FROM ex_case;

結果

id item_name price rank
001 商品A 1000 安物
002 商品B 5000 普通
003 商品C 10000 高級品

CASE式は、それ単体でも様々なことができて便利ですが、集約関数と組み合わせて使用することで更に使い道が広がります。
例えば、以下のようなテーブルがあったとします。

取引 金額
仕入 100
売上 200
仕入 300
売上 400

これを、取引ごとの合計を1レコードで取得して、以下のように表示したいとします。

仕入金額 売上金額
400 600

集計しているので、group by でできると一瞬思うかもしれませんが、group by ではそれぞれの集計結果が別レコードとして表示されるので、1レコードでまとめて取得することはできません。
この場合、以下のSQLで実現可能です。

SELECT 
sum(CASE 
        WHEN 取引 = '仕入' THEN 金額
        ELSE 0
    END) 仕入金額
, sum(CASE
        WHEN 取引 = '売上' THEN 金額
        ELSE 0
     END) 売上金額
FROM 取引テーブル;

sum以外にも、maxなどの集約関数にも入れ込むことができます。
CASEと集約関数の組み合わせは協力なので、使いこなせるようになっておく良いでしょう。


ウィンドウ関数

GROUP BY を使わずとも集約関数の結果を取得できる特殊な関数です。
分析関数などとも呼ばれます。
row_number, count, sum, max, rank, dense_rank などがあります。
使いこなせると非常に便利ですが、DBMSやバージョンによっては実装されていないものもあるので注意が必要です。
関数名() over(…) のように使用します。overの中にはpartition by句とorder by句が記述できます。

ウィンドウ関数のサンプルを見るために、こちらのSQL文にてテストデータを作成しておいてください。

create table ex_sales_window as 
select '2018/9/1' sales_date, 'X商店' customer,'商品A' item_name, 200 price, 100 quantity
union
select '2018/9/1' ,'X商店' ,'商品B' , 300 , 50
union
select '2018/9/1' ,'X商店' ,'商品C' , 500 , 10
union
select '2018/9/2' ,'Y商店' ,'商品A' , 200 , 40
union
select '2018/9/2' ,'Y商店' ,'商品B' , 300 , 150
union
select '2018/9/3' ,'Z商店' ,'商品C' , 500 , 80;
-- 書き方
関数名() over(partition by カラムリスト order by カラムリスト)
SELECT *
, row_number() over(ORDER BY sales_date, customer, item_name) 連番 -- 日付、顧客、商品の並びによる連番
, rank() over(ORDER BY price * quantity DESC) lank  -- 売上金額のランク
, sum(price * quantity) over(PARTITION BY sales_date ORDER BY item_name) 累計
, sum(price * quantity) over(PARTITION BY sales_date) 日付合計
FROM ex_sales_window;

結果

sales_date customer item_name price quantity 連番 売上ランク 累計 日付合計
2018/9/1 X商店 商品A 200 100 1 3 20000 40000
2018/9/1 X商店 商品B 300 50 2 4 35000 40000
2018/9/1 X商店 商品C 500 10 3 6 40000 40000
2018/9/2 Y商店 商品A 200 40 4 5 8000 53000
2018/9/2 Y商店 商品B 300 150 5 1 53000 53000
2018/9/3 Z商店 商品C 500 80 6 2 40000 40000

WITH句

WITH句は、SELECT文の結果に名前を付けることができる構文です。
一つのSQL文が長くなってしまう場合に、SELECT文の途中経過に名前を付けることで、全体のSQL文の可読性を上げることができます。

-- 書き方
WITH 名前 AS (SELECT文)
-- 例
-- all_itemsをテーブルのように扱うことができる。
WITH all_items AS (
    SELECT * FROM AStore_items
    UNION
    SELECT * FROM BStore_items
)
SELECT * FROM all_items;

SELECTのまとめ

SELECT文で使用できる構文は色々あるので、慣れるまでは組み合わせて使用するのが難しいかもしれません。
様々な構文を組み合わせる場合は書く順番がある程度決まっています。
以下にSQLの書き方をまとめます。

-- 基本構文
SELECT カラム1, ...            -- 取得したいカラムリスト
FROM テーブル名               -- レコードの取得対象のテーブル
JOIN句 結合対象テーブル   -- 結合対象のテーブル
ON 結合条件                       -- 結合条件
WHERE 絞込条件1 ...          -- レコードの絞り込み
GROUP BY カラム1, ...      -- 集約
HAVING 絞り込み条件       -- 集約後の絞り込み
ORDER BY カラム1, ...      -- 並び替え
LIMIT 絞り込み件数
OFFSET 読み飛ばし件数
; 

この形さえ覚えれば基本的には大丈夫でしょう。
集合演算はサブクエリは、それぞれのSELECT句が上記のような形になっていると考えればよいでしょう。

SELECTの書く順番

SELECT文をすらすら書くにはコツがあります。
書くときの順番が重要です。 書く順番としては、

  1. FROM句(メインテーブル)
  2. JOIN句(結合対象のテーブル)
  3. WHERE句
  4. GROUP BY句
  5. HAVING句
  6. ORDER BY句
  7. SELECT句

とすると書きやすくなります。
SELECT句はSQLの構造的には最初にありますが、SQL文を組み立てる時には最後に書いた方が良いです。
まずはどのテーブルのどのレコードが必要かを書いていき、取得したいデータの加工は最後に書く。
これに慣れてくると、SQLがすらすら書けるようになってきます。