SELECTステートメントは、1つもしくは複数のテーブルから、WHERE句などを併用し、データを抽出する、SQLにおけるデータ操作言語 (DML)ステートメントの1つである。
これは、データベースの1つもしくは複数のテーブルからデータを抽出するための命令で、データ操作言語 (DML)の中では最もよく使用される。プログラマはどのような結果を欲しいのかをSQL文で記述する必要はあるが、その結果を取得するためにどのような物理的な操作が実行されるのかを指示する必要はなく、データベースシステム(クエリオプティマイザ)がそのSQL文から最適なクエリプラン(実行計画)を作成する。
これらは、ANSI(米国規格協会)によってある程度は標準化されているが、それ以外に製品固有の命令文が多数存在するため、使用するには各種DBMSの仕様とバージョンを確認する必要がある。
なお、「テーブル」は「表」、「行」は「レコード」、「列」は「項目」と呼ぶこともある。
SELECT [ALL | DISTINCT] 列名 [,列名...]
FROM テーブル名 [,テーブル名...]
[WHERE 条件式]
[GROUP BY 列名 [HAVING 条件式]]
[ORDER BY 列名]
GROUP BY 列名[,列名...]
ORDER BY 列名[,列名...]
SELECT文において必須の構成句である。指定列を記述することで、列を射影する。*
で全列表示できる。また、算術演算子やグループ関数が使用可能である。
- 入力規則
- 列名入力規則の主なものとしては、
- 列名に使用できる文字は半角英数字および、$,#,_(アンダースコア)のみ。ただし、一部のDBMSにおける日本語版に限り全角文字が使用可能。
- 列名の先頭は半角アルファベットのみ。
- 文字上限は半角30文字まで。
- デフォルトは大文字で表示される。小文字などで表示したい場合は単一行関数を用いるか、" "で括るなどの処置が必要である。
- 各種DBMSにおける予約語は使用できない場合がある。
- 列別名
- (ASは省略可)で列別名表示ができる。ただし、WHERE句、GROUP BY句、HAVING句では、列別名の記述はできない。また、列名の表示に際して、デフォルトは半角英大文字である。これを小文字などで表示させたい場合、単一行関数の文字関数を使用するか、" "で括るかするとよい。
- ALL/DISTINCTの指定
-
- ALL
- テーブルに同じデータ行がある場合でもすべてのデータを返す。指定がない場合はALLが選択される。
- DISTINCT(UNIQUE)
- テーブルに同じデータ行がある場合、重複を取り除いた1件のみを返す。
- グループ関数で使用可能(Accessは不可)。
- 単一行関数
- 単一の行を操作する関数で、一つの行に対し、一つの結果を返していく関数。SELECT句、WHERE句、ORDER BY句で使用可能で、以下の種類がある。また、ネスト(入れ子)に制限はない。
- 文字関数
- 文字を操作する関数。主なものに以下のものがあるが、対応しているDBMSは各種異なっているため、注意が必要である。
- LOWER
- ほとんどのDBMSで対応。指定した文字列を小文字に変換する。
- 例:LOWER('ABC') → abc
- UPPER
- 指定した文字列を大文字に変換する。
- 例:UPPER('xyz') → XYZ
- INITCAP
- Oracle、postgreSQLで指定可能。指定した文字列の先頭のみを大文字に変換する。
- 例:INITCAP('WORLD')→ World
- INSERT
- MySQL、DB2で指定可能。指定した文字列に特定の文字列を置換、挿入する。(任意の文字列,挿入位置,置換を行う文字数《0の場合は、挿入》、挿入する文字)
- 例:INSERT('123',2,1,'to') → 1to3
- INSERT('worldnews',6,0,'weather') → worldweathernews
- CONCAT
- Oracle、MySQL、DB2で指定可能。指定した文字列を結合する。
- 例:CONCAT('blue','bird') → bluebird
- LEFT/RIGHT
- MySQL、SQLserverなどに対応。文字列の左/右部分を抽出する。
- SUBSTR
- Oracle、MYSQLなどに対応。指定した文字列を抽出する。(探索する文字列,探索開始の位置《負数の場合は文字列末尾から》,抽出する文字数《省略した場合は全て》)
- 例:SUBSTR('architecture',6,3) → tec
- INSTR
- Oracle、MySQL、Accessで指定可能。指定した文字列の位置を参照する。(探索する文字列,参照する文字《複数存在すると最初に探索された位置が参照される》)
- 例:INSTR('emperor','r') → 5
- LENGTH/LEN
- 指定した文字列の長さを参照する(間の空白も含む)。
- 例:LENGTH('Sri Jayawardenepura Kotte') → 24
- TRIM
- ほとんどのDBMSに対応。指定した文字を削除する。対応できるのは先頭と末尾のみである。
- 例:TRIM('S' FROM 'sword') → word
- REPLACE
- 指定の文字を置き換える(該当文字は全て置き換わる)。
- 例:REPLACE('encyclopedia','e','u') → uncyclopudia
- REPEAT/REPLICATE
- MySQL、PostgreSQLなどに対応。指定の文字を任意の数だけ繰り返す。
- 例:REPEAT('S',5) → SSSSS
- REVERSE
- MySQL、SQLserverに対応。文字列を反転させる。
- 例:REVERSE('Lee') → eeL
- SPACE
- MySQL、SQLserver、DB2などに対応。スペース文字列を作成する。
- などがある。
- 数値関数
- 数値を操作する関数。汎用されているものとしてROUND(四捨五入)、TRUNC(切り捨て)、MOD(剰余)などがある。ROUNDとTRUNCには引数に共通のルールがあり、(数値,n)において、n>=0の場合は小数点第(n+1)位を処理する。n<0の場合は10の(-n-1)乗の値を四捨五入する。
- ROUND
- 指定された値の四捨五入を行う。
- 例:ROUND(123.456,0) → 123
- ROUND(123.456,1) → 123.5
- ROUND(123.456,-1) → 120
- TRUNC
- 指定された値を切り捨てる。
- 例:TRUNC(123.456,-2) → 100
- MOD
- 除算における剰余を返す。(被除数,除数)
- 例:MOD(1000,30) → 10
- 日付関数
- 日付を操作する関数。各種DBMSによって大きく操作方法が異なるため、注意が必要。
- データ型変換関数
- 特定のデータ型を特定のデータ型に変換できる。同様に、各種DBMSによって使用可能な関数が異なる。
- グループ関数
- グループ化を行った行に対して一つの結果を返す関数。SELECT句(ただし、GROUP BY句で指定した列のみ)、HAVING句、ORDER BY句で使用可能。ネストは2段まで可能である。種類としてはAVG(平均値)、MAX(最大値)、MIN(最小値)、SUM(合計)、COUNT(返される行数)、STDDEV(標準偏差)などがある。
- COUNT
- 行数を返すものであって、合計値を返すものではない。合計値を返す場合はSUMを用いる。たとえば、正規化を行っていない販売実績(商品番号,商品名,販売日,販売個数)という表から、各商品における合計販売個数を求めようとした場合に
SELECT 商品名,COUNT(販売個数) FROM 販売実績 GROUP BY 商品名
とすると、各商品名の登場頻度、すなわち販売された回数(個数は不問)が返されてしまうことになる。
- また、前述のALL、DISTINCTが使用可能である。例文でCOUNT(DISTINCT 販売日)とした場合、日毎に販売された商品数が返されることになる(同じ日に売れた商品の重複が避けられるため。対して、省略、あるいはALLを使うと延べ数となる)。
- ※
COUNT(*)
とすると、NULL値を含めた全ての行数を返すが、COUNT(列名)
とした場合、NULL値は返さない。
- 集合演算
- 複数表から問合わせ結果を参照する複合問合わせを行うための演算子を用いた構文のこと。UNION(和集合)、UNION ALL(全集合)、INTERSECT(積集合)、EXCEPTまたはMINUS(差分集合)があるが、DBMS環境によって使用できないものや演算子の名称が異なるものもある。各演算子における優先順位はなく、左から右に、順番に処理される。
- 主な条件として、
- 問い合わせる行を一致させる必要がある。
- 各SELECT句に対応する行のデータ型の種類を一致させる必要がある(一部データ型において互換性は持つ)。
- データサイズは一致していなくても良い。
- ある条件を満たせば、列名は異なっていても構わない。
- UNION ALL以外はソートされて返される。デフォルトは前文の第1列が基準となる。
SELECT文において必須の構成句。FROM句では列参照に用いる表を指定する。複数の表から指定する場合、結合が行われ、JOINを用いるほか、WHERE句によって共通列の結合関係を指定することも可能(ただし、この場合は外部結合ができない。ただし、OracleDBMSの場合、(+)という特殊な記号を使用して、外部結合を行える)。なお、表を必要としないクエリを行う場合も、DUAL表のようなダミーの表を指定することとなるが、このような場合にFROMを省略できる拡張の行われた処理系も多く存在する。
このFROM句では表別名の指定が可能であるが、一旦表別名を指定した場合、表別名で記述してやらないとエラーになる。
複数の表から行を参照する場合に用い、JOINを利用することが多い。種類としては以下のものがある。
- クロス結合
- デカルト積を生成する。結合条件は指定できない。一部、対応していないDBMSもある。
- 等価結合
- 結合条件として、等価演算子(=)を用いる結合。一般的によく用いる結合方法。結合する共通列にnull値が存在しない場合に用いる。
- 等価結合の一般的な書式は以下の通りである。
SELECT 任意の表名.共通列 FROM 表名A [INNER] JOIN 表名B ON 表名A.共通列 = 表名B.共通列;
- ここで表名は記述の簡便化を図るため別名表示させることが多い。その場合、共通列には必ず表別名で記述しないとエラーになる。また、SELECT句において共通列は必ずどの表のものかを記述しないといけない(これを表修飾という)。その他の共通でない列は、必ずしも表修飾させなくても良いがパフォーマンスが低下するため、表修飾を習慣づけるのが望ましいとされている。
- ※二つ以上の共通列があり、片方のみを使用したい場合
- Oracle、MySQLなどに限りUSING句を用いると、結合に用いる共通列を限定することができる。例文は表A、Bに二つ共通列があるが、そのうちAだけを使用する場合の記述。
SELECT 共通列A,任意の表名.共通列B,その他の列名 FROM 表名A [INNER] JOIN 表名B USING(共通列A);
- この場合、共通列Aに対し、表修飾を行うとエラーになる。また、共通列Bにはどちらの表を使用するか表修飾を行わないと、同様にエラーになる。
- 非等価結合
- 結合条件として、等価演算子を用いない場合の結合。例としては>,<,<>(不一致),BETWEENなどがある。
SELECT 任意の表名.共通列 FROM 表名A [INNER] JOIN 表名B ON 表名A.共通列 <> 表名B.共通列;
- 例文では、共通列から同じ値を持たない行が返されるため、データの漏れなどを確認するときなどに重宝する。
- 自然結合
- 異なる二つ以上の表に用いると、同じ名前の共通列がある場合はそれに基づき結合を行う結合(ない場合でも自動でデカルト積を生成し、その場合はクロス結合と同じになる)。JOIN句ではNATURAL JOINと記述し、共通列を表別名で修飾する必要はない。逆に共通列を表名や表別名で修飾するとエラーになる。
- 自然結合は、ある表において、行の一部が外部キーとなっていた場合、その主キーが参照する値を表示したい場合などに用いると、SQLの記述が簡易になる。たとえば、企業(企業コード、企業名、業種コード)、業種(業種コード、業種名)という二つの表があり、企業表の業種コードは外部キーになっている。ここから企業名と業種名を抽出したい場合に自然結合を用いると
SELECT 企業名,業種名 FROM 企業 NATURAL JOIN 業種;
- このように、SQL文は非常に簡潔となる。しかし、等価結合と比較して無駄な動作が発生するために、パフォーマンスを考慮すると等価結合が望ましい場合が多い。
- 自己結合
- 同じ表にそれぞれ別名を用いて結合することで、広義の等価結合に属する。使用例としては、一つの表から相関的な値が使用されている時などに用いる。たとえば、表名A(棋士番号、棋士名、師匠番号)という表があり、師匠番号には棋士番号に対し外部キーが与えられている。ここで、各棋士と師匠の関係を一覧表示したい場合、棋士番号と師匠番号を関連づけて自己結合を行うと列の抽出を行える。
SELECT K.棋士名, S.棋士名 AS 師匠名 FROM 表名A K [INNER] JOIN 表名A S ON 表名K.師匠番号 = 表名S.棋士番号;
- ※ここでは、表名Aから Kという棋士一覧表と、Sという師匠一覧表を作りだし、表Kにおける師匠番号と表Sにおける棋士番号を結びつけ、最終的に表Kの棋士名と、表Sの棋士名を師匠名と別名表記して一覧表示している。なお、データベース構築においては、新たに師匠表を作り、その主キーと棋士表の師匠番号を外部キーとして結びつける方法もある(もし、師匠番号に棋士番号と異なる値が入力された場合は新たに表を作った方が無難である)。
- 外部結合
- 一方、あるいは双方の値にNULL値を含む場合に使用される。左側外部結合、右側外部結合、完全外部結合の3種類がある。書式は以下の通りである。
- 方向には(LEFT,RIGHT,あるいはFULLのいずれかを入れる。なお、DBMSによってはFULLに対応していないものもある)
SELECT 任意の表名.共通列 FROM 表名A (方向) [OUTER] JOIN 表名B ON 表名A.共通列 = 表名B.共通列;
- ここでは、全ての行を抽出したい側にその方向を記述(たとえば、左の表が完全《null値が存在しない》ならば、LEFT、その逆ならばRIGHT、双方にnull値が存在する場合は、FULL)する。
WHERE句は、データを抽出する選択条件式を指定する。単一式を用いるほか、複数条件を照会する副問合せが用いられることも多い。また、テーブル間結合を行う際には、その結合関係を指定する。このWHERE句ではグループ関数を使用してはいけないルールが存在する。
- パターンマッチング検索
- SQL文では、以下の図式を用いることで部分一致検索が可能である。パターンマッチに用いられるワイルドカードには「%」(0文字以上)「 _ 」(1文字)がある。書式は以下の通りである。
- WHERE 列名 LIKE '_A%'
- ここではAの前に一文字、Aの後に0文字以上の値が選択される(たとえばJAPAN,CAROL,CASTLE,5Aなど)。
- BETWEEN
- WHEREにおいて、一定の範囲内にある行を求めたい場合はBETWEENを用いると便利である。書式は以下の通りである。
- 列名 BETWEEN ( A AND B )
- これによって、A以上B以下に該当する行が選択される(必ず、以上、以下の関係になるため、未満、あるいは超を求めたい場合はOR演算子と不等号を用いる必要がある)。
- IN
- 副問合せの項にて後述
- NULL処理
- もし、条件式の値が空値の場合を求める場合は、NULLを用いる。書式は以下の通りである。
- WHERE 列名 IS NULL
- ※ここで、列名 = NULLとするとエラーになる。また、NULLは値が存在していない状態であり、値にスペースが置かれていた場合はNULLとならない。
GROUP BY句は、グループ化する列名または列名を含んだ式を指定する。列別名は使用できない。また、SELECT文でGROUP BY句で、グループ関数を除き、グループ化する列以外の列が存在した場合、一緒に記述する必要がある。
例: SELECT A. B FROM LIST GROUP BY A, B;
(グループ化するのはAだが、B列も一緒に記述しないとエラーとなる)。
また、GROUP BY句を含むとビューの作成ができない(SELECT文でグループ関数を使用した場合、あるいはDISTINCT句を使用した場合も同じ)。
HAVING句は、GROUP BY句で集計した結果に対して抽出条件を指定する際に使用する。グループ関数の使用が可能である。順序はGROUP BY句と前後になっても問題ない。
ORDER BY句は、ソートする列または列を含んだ式を指定するもので、いかなる場合でも構文の最後に指定する。カンマで区切ることで、複数列を指定できる。通常はASC(ascending order)、すなわち昇順指定であり、降順指定したい場合はDESC(descending order)と表記する。また、列別名での指定が可能なほか、序数での指定が可能である(たとえば、2番目の列で参照したい場合、ORDER BY 2とできる)。
ここでのNULLはDBMSに依存し、最大値をとるものと最小値をとるものがある。
副問合せは値が一定でない値に対して問合わせが可能であり、必ず( )で囲む必要がある。FROM句、WHERE句、HAVING句で使用可能。FROM句で指定されていない別表を参照することも可能であり、また、ネストにも制限はない(ただし、パフォーマンスは低下するおそれがある)。
副問合せに対して単一行を選択したい場合は比較演算子を用いる。この場合、複数行が検索されればエラーとなる。もし、複数行を選択したい場合は、一般にIN、他にANY(SOME)+比較演算子、ALL+比較演算子、またはEXISTSを用いる。いずれの式も頭にNOTを付記すれば、否定表現となる。この比較条件の記述はDBMSの環境に依存していることが多い。
- IN
- INは、括弧内にいずれかの値が入っている場合に限り、真を返す演算子である。たとえば商品(商品番号,商品名,…)という表から、商品番号が3と8の商品名を抽出するとする。この場合はSELECT 商品名 FROM 商品 WHERE 商品番号 = 3 OR 商品番号 = 8と記述してもよいが、INを用いることで
SELECT 商品名 FROM 商品 WHERE 商品番号 IN (3,8)
と簡潔に表すことができる。これを副問合せに応用することも可能で、たとえば納品(伝票番号,行番号,商品番号,納品日,個数,…)という表から一度でも納品された商品を表したい場合は
SELECT 商品名 FROM 商品 WHERE 商品番号 IN (SELECT 商品番号 FROM 納品)
とすれば、一度でも納品された商品が抽出される。逆に、NOT INとすると納品表に一度も納品されていない商品が抽出される。
- EXISTS
- EXISTSはその値が存在するかを探索する演算子であり、存在する場合は真を返す。前述の例をEXISTSで記述する場合はこのようになる。
SELECT 商品名 FROM 商品 WHERE EXISTS (SELECT * FROM 納品 WHERE 商品.商品番号 = 納品.商品番号)
結果的に、INで記述した場合と同じ結果を示すことになるが、前述の例とは実行順序が異なっている。通常、副問合せを先に実行しその結果を使って主問合せを実行するが、後述の例の場合、副問合せ内で副問合せ内にない表(商品表)を参照しているので副問合せを先に実行できない。このように、そのFROM句にない表の列を参照する副問合せを「相関副問合せ」と言い、その場合は、主問合せの表の行ごとに副問合せが繰り返し実行される。ただし、その分パフォーマンスは低下してしまうため、実用上は前述の例のように記述した方がよい。
MySQL、PostgreSQLなど一部のDBMSで使用可能な構成句。ORDER句の後に記述すると、選択する行を限定できる。文法はLIMIT(開始の行,取得する行数)、またはLIMIT 取得する行数 OFFSET 開始の行。たとえば、受験者(生徒番号,生徒名,国語,数学,英語)という表から高得点者上位10人だけを抽出したい場合は以下のように記述する(国語、数学、英語には数値が入る)。
SELECT 生徒名,SUM(国語+数学+英語) AS 成績 FROM 生徒 ORDER BY 成績 DESC LIMIT 1 , 10
- ※該当者が11人以上存在した場合は、生徒番号の昇順が第2の並べ替え基準となる。また、省略してLIMIT(n)とすると、最初から抽出する行数を指定することになる。
テーブル "T"
|
SQL文
|
結果
|
|
SELECT * FROM T;
|
|
|
SELECT C1 FROM T;
|
|
|
SELECT * FROM T WHERE C1 = 1;
|
|
|
SELECT * FROM T ORDER BY C1 DESC;
|
|
右図のテーブル"T"欄のテーブルに対して、SQL文 SELECT * FROM T;
を実行すると、結果欄に示されているように、テーブル"T"のすべての行とすべての列が返される。
同じテーブル"T"に対して、SQL文 SELECT C1 FROM T;
を実行すると、テーブル"T"のすべての行の列C1のみが返される。これは、関係代数 における「射影」に相当する。
同じテーブル"T"に対して、SQL文 SELECT * FROM T WHERE C1 = 1;
を実行すると、列C1 の値が 1 であるすべて行のすべての列が返される。これは、関係代数 における「選択」に相当する。
最後のSQL文 SELECT * FROM T ORDER BY C1 DESC;
は最初のSQL文と同じ行を返すが、並び順が、ORDER BY句により、C1に対して降順(Z-A)になる。ORDER BY句で複数列を指定する場合は、カンマで区切る。例:ORDER BY C1 ASC, C2 DESC(この場合、列C1に対しては昇順、列C2に対しては降順になる。)