SELECT文専用の修飾は、大きくとらえれば、どれも「SELECT文によって得られた検索結果を様々な形に加工するためのもの」です。多くのDBMSでは、SELECTによる検索とともに加工も行われるが、2段階の処理と考えるとイメージしやすい。
ステップ①通常の検索実行。ステップ②検索結果を加工(並べ替えや重複行の排除など)。
キーワード | 内容 |
---|---|
DISTINCT | 検索結果から重複行を除外する |
ORDER BY | 検索結果の順序を並び替える |
LIMIT | 検索結果から件数を限定して取得する |
UNION | 検索結果にほかの検索結果を足し合わせる |
EXCEPT | 検索結果からほかの検索結果を差し引く |
INTERSECT | 検索結果とほかの検索結果で重複する部分を取得する |
DISTINCTキーワードをSELECT文に付加すると、結果表の中で内容が重複している行があれば、その重複を取り除いてくれます。
★重複行を除外する
SELECT DISTINCT 列名・・・
FROM テーブル名
DISTINCTは、データの種類を取得したい場合に役立ちます。家計簿の場合「食費」「娯楽費」「交際費」などの支出の記録が何度も登場することが考えられます。このとき、DISTINCTを使って重複を取り除くことで、どのような種類の支出があったかを、一覧で抽出することができます。
費目一覧の取得
SELECT DISTINCT 費目 FROM 家計簿
このDISTINCT修飾は、ほかの修飾と異なり、SELECT文の最初に記述する必要があります。
SELECT文の最後にORDER BY句を記述すると、指定した列の値を基準として、検索結果を並び替えて取得することができます。
★検索結果を並び替える
SELECT 列名・・・FROM テーブル名
ORDER BY 列名 並び順
並び順を昇順にする場合はASC、降順にする場合はDESCを指定します。初期値は昇順なので指定を省略すると、結果は昇順になります。ORDER BY句に文字列を指定すると、DBMSに設定された照合順序(文字コード順、アルファベット順など)を基準として並び替えられる。
出金額で昇順となるように並べ替えて取得する
SELECT * FROM 家計簿
ORDER BY 出金額
*降順で取得する場合は、出金額の後ろにDESCを付ける。
ORDER BY句による並び替えの際、複数の列をカンマで区切って指定することができます。最初に指定された列で並び替えて同じ値が複数行あれば、次に指定された列で並び替えが行われます。
また、ORDER BY句では、並び替えの基準とする列を列名ではなく列番号で指定することも可能です。列番号とは、選択リストにおける列の順番のことで、SELECT命令に記述した順に1から数えます。テーブルの前列を指定するアスタリスクを使った場合も、実際に取得の対象となる列に置き換えた列番号を指定します。
ORDER BY句における列指定に列番号を用いる場合、SELECT文の選択列リストの記述を修正すると並び替えの結果にも影響が及ぶ点には注意が必要です。このような落とし穴もあることから、列番号指定を用いる機会はあまり多くないでしょう。
検索結果の全行ではなく、先頭の一部の行だけを得られればよいケースもあります。そのような場合、SELECT文にLIMIT句を付けることによって簡単に実現できます。
★先頭から数件だけを取得する
SELECT 列名・・・FROM テーブル名
( ORDER BY 列名・・・)
LIMIT 取得行数 (OFFSET 先頭から除外する行数)
LIMIT句には、全部で何件のデータを取得するかを整数値で指定します。OFFSETは、先頭からではなくデータの途中から数えたい場合に任意で指定します。例えば、結果の11~15番目の行だけを取得したい場合は、LIMIT句に5、OFFSETに10を指定することになります。
出金額の高い順に3件取得する
SELECT 費目, 出金額 FROM 家計簿
ORDER BY 出金額 DESC LIMIT 3
3番目に高い出金額だけを取得する場合、LIMIT句の後ろにOFFSET 2を付ける。
LIMIT句はORDER BY句と併用されることが多い機能ですが、LIMIT句だけでも使用することが可能です。ただしその場合は、どのような並び順で返ってくるかは実行してみるまでわりません。
長年データベースを利用すると、テーブルに格納する行数が膨大になり、処理が遅くなってしまう恐れがあります。そこで、これまでのデータを2つのテーブルに分けて管理することにします。しかし、分けたテーブルに対し1回ずつ、都合2回も同じSELECT文を実行するのは、面倒です。構造がよく似た複数のテーブルにSELECT文をそれぞれ送り、その結果を組み合わせたい場合は、集合演算子を活用することにより、1つのSQL文で目的を達成することができます。
集合演算とは、SELECT命令によって抽出した結果表を1つのデータの集合ととらえ、その結果同士を足し合わせたり、共通部分を探したりといった様々な演算を行ってくれるしくみです。
UNION演算子は、最も代表的な集合演算子です。2つのSELECT文をUNIONでつないで記述すると、それぞれの検索結果を足し合わせた結果(和集合)が返されます。
★2つのSELECT文の結果を足し合わせる
SELECT 文1
UNION (ALL)
SELECT 文2
UNIONにALLというキーワードを付加すると、和集合の結果に重複行があった場合に動作が違ってきます。UNIONでは重複行を1行にまとめるのに対し、UNION ALLでは重複行をすべてそのまま返します。
和集合を取得する
SELECT 費目, 入金額, 出金額 FROM 家計簿
UNION
SELECT 費目, 入金額, 出金額 FROM 家計簿アーカイブ
ORDER BY 2, 3, 1
★集合演算子を使える条件
SELECTの結果を集合演算子でまとめるときは、選択列リストの列数とそれぞれのデータ型が一致していなければならない。
つまり、列数とデータ型さえ一致していれば、まったく異なるテーブルや列でもひとまとめにして抽出できる。1つのテーブルに格納されたデータを複数の異なる条件で抽出したい場合にもUNIONは活用できます。それぞれのWHERE条件を記述したSELECT文を用意し、UNIONで1つのSQL文としてまとめることで、SQLの実行回数を抑えることができます。
★集合演算子でORDER BY句を使うときの注意点
・ORDER BY句は最後のSELECT文に記述する。
・列番号以外による指定(列名やASによる別名)の場合、1つめのSELECT文のものを指定する。
*選択列リストの数が合わないSELECT文で、どうしても集合演算子を使いたい場合、足りない方の選択列リストにNULLを追加することで、数を一致させることができる。
差集合は、ある集合と別の集合の差です。あるSELECT文の検索結果に存在する行から、別のSELECT文の検索結果に存在する行を差し引いた集合となります。差集合を得るには、EXCEPT演算子を用います。
★2つのSELECT文の結果の差を得る
SELECT 文1
EXCEPT (ALL)
SELECT 文2
*Oracle DBではEXCEPTの代わりにMINUSというキーワードを用います。
差集合を求める場合は、SELECT文の順番に注意が必要です。和集合は、各集合に存在するすべての要素の集合なので、A UNION B でも、B UNION A でも、結果に影響はありません。しかし差集合は、どの集合を基準とするかによって結果が変わってきます。これは、1+2と2+1の結果は同じでも1-2と2-1の結果は異なるのと同様です。
差集合を取得する
SELECT 費目 FROM 家計簿
EXCEPT--Oracle DBやMySQLでは非対応
SELECT 費目 FROM 家計簿アーカイブ
EXCEPTとMINUSも、「集合演算子を使える条件」を満たす必要があります。ORDER BY句を使うときの注意点も同様です。
INTERSECT演算子で求めることができる積集合とは、2つのSELECT文に共通する行を集めた集合です。
★積集合を求める
SELECT 列名・・・ FROM テーブル名
INTERSECT(ALL)
SELECT 列名・・・ FROM テーブル名
*積集合は和集合と同じく、どの順番でSELECT文を記述しても結果は変わりません。
積集合を取得する
SELECT 費目 FROM 家計簿
INTERSECT (ALL)
SELECT 費目 FROM 家計簿アーカイブ
「出金額 + 100」のような、結果がTRUEやFALSEにならない式を計算式と呼ぶことにします。計算式も、評価される結果に「化ける」点では条件式と同じです。
SELECT文において、SELECTのすぐ後ろに指定するのが選択リストです。選択リストは、結果表にどのような列を出力するかを指定する役割があります。テーブルの列名以外に、固定値や計算式を指定することも可能です。
選択リストへの様々な指定
SELECT 出金額, --列名での指定
出金額 + 100, --計算式で指定
'SQL' --固定値での指定
FROM 家計簿
★選択列リストへの指定と結果
列名 :列の内容がそのまま出力される
計算式:計算式の評価結果が出力される
固定値:固定値がそのまま出力される
*積集合は和集合と同じく、どの順番でSELECT文を記述しても結果は変わりません。
選択リストに計算式や固定式を使うと、その計算式などがそのまま結果表の列名になってしまいます。このような表示が好ましくない場合、ASを使うことで列の別名を使えます。
INSERT文での計算式の利用
INSERT INTO 家計簿 (出金額)
VALUES (1000 + 105)
出金額に格納する値として、「1000+105」という計算式を指定しています。これは、直接「1105」を指定した場合と同じ結果になります。
UPDATE文での計算式の利用(列を含む)
UPDATE 家計簿
SET 出金額=出金額 + 100
出金額の新たな値として、「出金額 + 100」という計算式を指定しています。「出金額を現在の額より100円増加させる」というUPDATE文になります。
★混乱しやすい列指定のとらえ方
・式中の「出金額」とは、テーブル内の出金額を指している。
・出金額の列には、複数の値(0、7560、5000・・・)が入っている。
・よって、「出金額 + 100」は、複数の値と100の足し算になる。
・複数の値と100を足すことなんてできないのでは?
このように混乱しないために、次の原則をしっかり理解する必要があります。
★DBMSによる処理の原則
DBMSは、テーブル内の各行を1つずつ順番に処理していく。
式の評価なども、各行ごとに行われる。
UPDATE文は「1回の処理で出金額列がすべて書き換わる」ととらえるべきではありません。DBMSは、「1行に注目しては、出金額を計算して更新する」という処理を、行数分繰り返しているのです。
演算子 | 使い方 | 説明 |
---|---|---|
+ | 数値 + 数値 | 数値同士で足し算する |
日付 + 数値 | 日付を指定日数だけ進める | |
- | 数値 - 数値 | 数値同士で引き算をする |
日付 - 数値 | 日付を指定日数だけ戻す | |
日付 - 日付 | 日付の差の日数を得る | |
* | 数値 * 数値 | 数値同士で掛け算をする |
/ | 数値 / 数値 | 数値同士で割り算をする |
|| | 文字列 || 文字列 | 文字列を連結する |
CASE演算子は、列の値や条件を評価し、その結果に応じて好きな値に変換することができる。
出金額の新たな値として、「出金額 + 100」という計算式を指定しています。「出金額を現在の額より100円増加させる」というUPDATE文になります。
★CASE演算子の利用構文①
CASE 評価する列や式 WHEN 値1 THEN 値1のとき返す値
(WHEN 値2 THEN 値2のとき返す値)・・・
(ELSE デフォルト値)
END
★CASE演算子の利用構文②
CASE WHEN 値1 THEN 値1のとき返す値
(WHEN 値2 THEN 値2のとき返す値)・・・
(ELSE デフォルト値)
END
多くのデータベースには、より高度な処理を行いやすくするために関数と総称される命令がたくさん準備されています。すべての関数は「呼び出し時に指定した情報(引数)に対し、定められた処理を行い、結果(戻り値)に変換する」という動作をします。
★関数について定められていること
名前 : その関数の名前
引数 : その関数を呼び出す際に引き渡す情報(関数によっては2つ以上の場合もある)
戻り値 : その関数の呼び出し結果として得られる情報
★LENGTH関数の仕様
名前 :LENGTH
引数 :文字列が格納された列(または式)
戻り値:文字列の長さを表す数値
メモとメモの長さを併せて表示させる
SELECT メモ , LENGTH (メモ) AS メモの長さ
FROM 家計簿
式の評価と同様に、関数の呼び出しも各行ごとに繰り返し行われています。関数の呼び出し記述は、呼び出し完了後に戻り値に「化けます」。この特性を利用して、関数の呼び出しを入れ子にすることもできる。
●関数はDBMS製品ごとの違いが大きく互換性が少ない分野です
テーブルの列に格納されている文字列の長さを取得したり、文字列の長さで絞り込み検索を行いたい場合に利用します。
★文字列の長さを得る関数
LENGTH(文字列を表す列) ⇒ 文字列の長さを表す数値
LEN(文字列を表す列) ⇒ 文字列の長さを表す数値
10文字(10バイト)以下のメモだけ取得する
SELECT メモ , LENGTH (メモ) AS メモの長さ
FROM 家計簿
WHERE LENGTH(メモ) <= 10
テーブルの列に格納されている文字列の長さを取得したり、文字列の長さで絞り込み検索を行いたい場合に利用します。
★空白を除去する関数
TRIM(文字列を表す列) ⇒ 左右から空白を除去したい文字列
LTRIM(文字列を表す列) ⇒ 左側の空白を除去したい文字列
RTRIM(文字列を表す列) ⇒ 右側の空白を除去したい文字列
CHAR(10)型の列に対して'abc'という文字列を格納すると、7文字分の空白が右側に自動的に追加され'abc 'という文字列として格納されてしまいます。そのような文字列をSELECT文でそのまま抽出すると、abcの後ろに空白が付いた状態で取得してしまいます。TRIM関数を使うことで、余計な空白を簡単に削除できます。
空白を除去したメモを取得する
SELECT メモ , TRIM (メモ) AS 空白除去したメモ
FROM 家計簿
REPLACE関数は、文字列の一部を別の文字列に置換する関数です。例えば。文字列「axxle」の「x」を「p」に置換し、「apple」とすることができます。
★文字列を置換する関数
REPLACE(置換対象の文字列, 置換前の部分文字列, 置換後の部分文字列)
⇒ 置換処理された後の文字列
メモの一部を置換する
UPDATE 家計簿
SET メモ = REPLACE(メモ, '購入', '買った')
文字列の一部分だけを取り出したい場合には、SUBSTRING関数またはSUBSTR関数を利用します。どちらを利用できるかは、DBMS製品によって異なります。
★文字列の一部を抽出する関数
SUBSTRING( 文字列を表す列, 抽出を開始する位置, 抽出する文字の数)
⇒ 抽出された部分文字列
SUBSTR( 文字列を表す列, 抽出を開始する位置, 抽出する文字の数)
⇒ 抽出された部分文字列
*抽出を開始する位置には1以上の数字を指定する。
費目列の1~3文字目に「費」があるものだけを抽出
SELECT * FROM 家計簿
WHERE SUBSTRING(費目, 1, 3) LIKE '%費%'
小数の取り扱いや金額計算などでよく見られる数値の丸め処理も、関数で用意されています。ROUND関数は、指定した位置で四捨五入した結果を返す関数です。
★指定桁で四捨五入する関数
ROUND(数値を表す例, 有効とする桁数)
⇒ 四捨五入した値
*「有効とする桁数」に指定する値が正の場合は小数部の桁数、負の場合は整数部の桁数
*抽出を開始する位置には1以上の数字を指定する。
百円単位の出金額を取得
SELECT 出金額, ROUND(出金額, -2) AS 百円単位の出金額
FROM 家計簿
*出金額の下2桁目、10の位で四捨五入される。
四捨五入ではなくて切り捨てたい場合には、TRUNC関数を使います。使い方はROUND関数と同じです。
★べき乗を計算する関数
POWER(数値を表す例, 何乗するかを指定する数値)
⇒ 数値を指定した回数だけ乗じた結果
*抽出を開始する位置には1以上の数字を指定する。
SQLで現在の日付を得るにはCURRENT_DATE関数、現在の時刻を得るにはCURRENT_TIME関数を用います。
★現在の日時を得る関数
CURRENT_DATE ⇒ 現在の日付
CURRENT_TIME ⇒ 現在の時刻
*引数が不要のため、関数名の後ろに()は付記しない。
日付を自動的に取得して登録する
INSERT INTO 家計簿
VALUES (CURRENT_DATE, '食費', 'ドーナツも買った', 0, 260)
*出金額の下2桁目、10の位が四捨五入される。
ある型のデータを別の型として扱いたい場合に、CAST関数を使います。
★データ型を変換する関数
CAST(変換する値 AS 変換する型) ⇒ 変換後の値
例えば、出金額の列はINTEGER型ですが、末尾に「円」という文字列を連結して表示したい場合、「出金額 + '円'」としたいところですが、数値と文字列という型の異なる値を||演算子で連結するのは、定義されていない演算なので、DBMS製品によっては動作が異なる可能性があるため少し不安です。そこで、「CAST(出金額 AS VARCHAR(20)) + '円'」のように、文字列型に型を揃えてから連結する方法が確実です。
★最初に登場するNULLでない値を返す関数
COALESCE(列や式1, 列や式2, 列や式3・・・)
⇒ 引数のうち、最初に現れたNULLでない関数
*引数は任意の数を指定可。ただし、すべての引数の型が一致している必要がある。もしすべての引数がNULLの場合、戻り値はNULL。
COALESCE関数は、「複数の引数を受け取り、受け取った引数を左から順番にチェックし、その中から最初に見つかったNULLではない引数を返す」という動作をする関数です。
COALESCE関数の基本動作
SELECT COALESCE('A', 'B', 'C'); /* 結果は 'A' */
SELECT COALESCE('NULL', 'B', 'C'); /* 結果は 'B' */
SELECT COALESCE('NULL', 'B', 'NULL'); /* 結果は 'B' */
SELECT COALESCE('NULL', 'NULL', 'C'); /* 結果は 'C' */
SELECT COALESCE(数値型の列, 0);
⇒ 数値型の列が出力される。ただし、NULLが格納されている場合は0になる。