備忘録的プログラミングリファレンス

SELECT クエリ

 SELECT クエリは、記録されたテーブルデータを閲覧するためのコマンドです。SQL の基本となる SQL 文のひとつです。SQL データ文とも呼ばれます。

 テーブルには表計算のように行列の概念があり、SELECT クエリは指定された行を検索し出力します。

SELECT クエリ
SELECT * FROM table_name;

 この例は table_name というテーブルに記録されている全データを行列で返します。

 クエリとは、問い合わせを意味し、データベースではデータの検索( SELECT )、入力( INSERT )更新( UPDATE )削除( DELETE )といった命令をサーバーに送る行為を指します。
 クエリには、作成( CREATE )、変更( ALTER )、削除( DROP )といった SQL スキーマ文も含みます。

構文(Syntax)

 SELECT 文はデータベースに記録されたデータを取得することができる SQL 文です。リテラル値や関数による結果を取得することもできます。

SELECT 文の簡単な例
SELECT * FROM table_name;

 WHERE といった条件によってデータを抽出することができます。

MySQL

 SELECT 文は MySQL では以下のような仕様になっています。

SELECT
    [ALL | DISTINCT | DISTINCTROW ]
    [HIGH_PRIORITY]
    [STRAIGHT_JOIN]
    [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
    [SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
    select_expr [, select_expr] ...
    [into_option]
    [FROM table_references
      [PARTITION partition_list]]
    [WHERE where_condition]
    [GROUP BY {col_name | expr | position}, ... [WITH ROLLUP]]
    [HAVING where_condition]
    [WINDOW window_name AS (window_spec)
        [, window_name AS (window_spec)] ...]
    [ORDER BY {col_name | expr | position}
      [ASC | DESC], ... [WITH ROLLUP]]
    [LIMIT {[offset,] row_count | row_count OFFSET offset}]
    [into_option]
    [FOR {UPDATE | SHARE}
        [OF tbl_name [, tbl_name] ...]
        [NOWAIT | SKIP LOCKED]
      | LOCK IN SHARE MODE]
    [into_option]

into_option: {
    INTO OUTFILE 'file_name'
        [CHARACTER SET charset_name]
        export_options
  | INTO DUMPFILE 'file_name'
  | INTO var_name [, var_name] ...
}

PostgreSQL

 PostgreSQL では以下のうような仕様になっています。

SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
    * | expression [ [ AS ] output_name ] [, ...]
    [ FROM from_item [, ...] ]
    [ WHERE condition ]
    [ GROUP BY expression [, ...] ]
    [ HAVING condition [, ...] ]
    [ WINDOW window_name AS ( window_definition ) [, ...] ]
    [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
    [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
    [ LIMIT { count | ALL } ]
    [ OFFSET start [ ROW | ROWS ] ]
    [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]
    [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ]

句について

 SELECT 文はいくつかの句で構成されています。例えば以下のような句があります。

 はデータベースによって文節とも呼ばれます。Oracle では、MySQLやPostgreSQL ではと呼ばれることが多いようです。
 このサイトではと呼ぶようにしています。

SELECT 句

 一番簡単な SELECT 文は、以下のようなテーブルの全データを取得します。*がすべてのカラムを対象にするという指定です。

SELECT * FROM table_name;

 上記の例では FROM 句もあります。FROM 句の対象はテーブルです。
 FROM がある場合には、FROM に指定されたテーブルの全てのデータを対象にするという前提になります。

 カラムを指定するには,で区切ってそのカラム名を並べます。結果が指定されたカラムの列データになります。

カラムの指定
SELECT id, name FROM table_name;

 FROM 句はない場合もあります。例えば以下のような組み込み関数を参照する場合には FROM 句は必ずしも必要ではありません。

現在の日時を取得
SELECT now();

 SELECT の直後はカラム名の指定だけではなく計算式、数値や文字列といったリテラル値が指定できます。

計算式やリテラル値
mysql> SELECT user, host, date( password_last_changed ), 'test' FROM mysql.user;

+------------------+-----------+-------------------------------+------+
| user             | host      | date( password_last_changed ) | test |
+------------------+-----------+-------------------------------+------+
| debian-sys-maint | localhost | 2023-06-12                    | test |
| mysql.infoschema | localhost | 2023-06-12                    | test |
| mysql.session    | localhost | 2023-06-12                    | test |
...

 date( password_last_changed ) は password_last_changed から日付だけを取り出します。'test' は文字列、リテラル値です。

 カラム名は AS 句によって変更することができます。

計算式やリテラル値
mysql> SELECT user, host, date( password_last_changed ) AS password_changed_date, 'test' AS 'test_literal' FROM mysql.user;

+------------------+-----------+-----------------------+--------------+
| user             | host      | password_changed_date | test_literal |
+------------------+-----------+-----------------------+--------------+
| debian-sys-maint | localhost | 2023-06-12            | test         |
| mysql.infoschema | localhost | 2023-06-12            | test         |
| mysql.session    | localhost | 2023-06-12            | test         |
...

FROM 句

 FROM には1つのテーブルの他に一時テーブル、ビュー、結合されたテーブルも指定できます。FROM に指定できるのは行列(テーブル)で表されたデータ群が指定できます。

一時テーブル

 一時テーブルにはサブクエリによって取得した行列データがあります。

サブクエリ
SELECT t.id FROM ( SELECT id, name FROM table_name ) AS t;

 上記の例はあまり意味がありませんが、以下のテーブルの結合でこのような書き方を使用します。

テーブルビュー

 テーブルビューは予めテーブルデータの取得方法を定義しておくものです。

ビュー
CREATE VIEW table_name_view AS
	SELECT * FROM table_name;

 ビューもテーブルと同じように FROM で指定できます。

ビューを参照
SELECT * FROM table_name_view;

 ビューは以下のテーブルを結合した状態を予め定義しておくことができます。

テーブルの結合

 テーブルの結合は複数のテーブルを1つのテーブルとして定義できるものです。以下は内部結合と呼ばれ複数のテーブルで一致する部分だけが取得できます。

テーブルの結合
SELECT * FROM table_name
	INNER JOIN join_table AS j ON table_name.id = j.table_id ;

 テーブルの結合を定義した SQL 文を毎度編集するのは大変です。ビューを作成することによってそのビューを参照するだけで結合されたテーブルを取得することができます。

テーブルの結合
CREATE VIEW table_name_view AS
	SELECT * FROM table_name
		INNER JOIN join_table AS j ON table_name.id = j.table_id ;

 テーブルの結合はサブクエリー(副問合せ)でも使用されます。

WHERE 句

 条件を指定した行データの抽出をする場合は WHERE 句を加えます。
 この WHERE 句によってテーブルデータから条件にあった行のみが対象になります。

SELECT * FROM table_name WHERE id <= 100;

 上記の例は id が 100 以下のレコードを検索して取得します。

 詳しくはWHERE 句ページを参照してください。

GROUP BY 句

 GROUP BY 句は重複するカラムのデータを対象にして行をグループ化します。集計などに使用されます。

ORDER BY
SELECT name, COUNT(*) FROM table_name GROUP BY name;

 注意点として取得するカラムにグループ化できないものを含むことができません。 例えば、併せてレコードidを取得するしようとするとグループ化によって対象にできないカラムがあるというエラーが返されます。

GROUP BY のエラー( MySQL )
> SELECT id, goods_id FROM sales_slip GROUP BY goods_id;
ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.sales_slip.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

 また、GROUP BY 句によってグループごとに分けられた行の検索には HAVING 句を使用します。

 詳しくはGROUP BY 句ページを参照してください。

HAVING 句

 HAVING 句は GROUP BY 句で使用する条件による抽出(フィルタリング)のために使用します。
 WHERE による条件は FROM で指定されたテーブルに機能し、GROUP BY で集約されるデータには機能しません。
 GROUP BY による集約されるデータに抽出条件が機能するようにするには HAVING 句を利用します。

ORDER BY
SELECT *, count(*) FROM table_name GROUP BY id
HAVING count(*) > 2;

 上記の例では、カラムに集約関数、count() を追加し、HAVING 句で同じ id をもつ行が2より大きい場合を抽出します。

ORDER BY 句

 ORDER BY 句は並べ替えを行います。カラムのデータによって昇順、降順で並べ替えができます。
 ORDER BY 句で並びを指定しない限りは、クエリによる結果セットの並びは決まっていません。ORDER BY 句を使用することでデータは明確に並び替えをすることができます。

ORDER BY
SELECT * FROM table_name ORDER BY id,name;

 ORDER BY 句はデフォルトで昇順で並び替えが行われます。DESC を指定することで降順に指定することができます。

LIMIT 句

 LIMIT 句は、検索されたレコードから指定された数の行を返します。

SELECT * FROM table_name LIMIT 100;

 上記の例では、検索された順で 100 行までを返してきます。レコードが 100 行に満たない場合は検索されたすべての行を返してきます。

 取得する行数の開始位置は OFFSET 句で指定します。例えば、101 行目から 100 行分を取得するには以下のようにします。

SELECT * FROM table_name LIMIT 100 OFFSET 100;

 返されたるレコードは、ORDER BY 句によってレコードを並べないと常に順番が安定しないかもしれません。LIMIT 句を利用する場合は ORDER BY 句を使用した方がよいでしょう。

SELECT * FROM table_name LIMIT 100 OFFSET 100 ORDER BY id;

CASE 句

 CASE 句を使用することで条件分岐を加えることができます。

SELECT id,
	CASE table_name.gender
		WHEN gender = 'M' THEN 'Mr.'
		WHEN gender = 'F' THEN 'Ms.'
	END;
FROM table_name;

 上記のクエリを実行すると、gender が 'M' では 'Mr.' を、 gender = 'F' では 'Ms.' を返します。

テーブル結合

 テーブルの結合は JOIN 句や INNER JOIN 句を使用して複数のテーブルを組み合わせます。

 JOIN 句に ON を付加することで任意の行のみを抽出します。

SELECT * FROM goods AS g JOIN sales_slip AS s
ON g.id = s.goods_id;

 テーブルの結合には外部結合と内部結合があります。JOIN 句はデフォルトの設定では内部結合です。
 JOIN 句の代わりに INNER JOIN 句によって明確に内部結合であることを指定することもできます。

 テーブル結合について詳しくはテーブルの結合 JOINを参照してください。

その他

DISTINCT 句 重複データを整理

 重複したデータを整理した形でデータ返させるには DISTINCT 句を使用します。
 DISTINCT 句は重複するデータを整理したいカラム名の直前に編集します。

重複データの整理
SELECT DISTINCT user_id FROM account;

 DISTINCT 句とは別に ALL 句があります。ALL はデフォルトでの指定と同じになります。

日時要素のみを取得

 日付・時刻データから年度や月度、日付のみを取り出すには EXTRACT() 関数を使用します。

EXTRACT() 関数
> SELECT
 	EXTRACT( YEAR FROM date_time ) AS year,
 	EXTRACT( MONTH FROM date_time ) AS month
 	FROM sales_slip;
+------+-------+
| year | month |
+------+-------+
| 2023 |    10 |
| 2023 |     9 |
| 2023 |     9 |
| 2023 |     9 |
| 2023 |     9 |
+------+-------+

 EXTRACT()は日時要素を取得するための関数です。EXTRACT( 日時要素 FROM 日時データ )といった使い方をします。
 年度は YEAR、月度は MONTH、日にちは DAY です。時間は HOUR、分は MINUTE、秒は SECOND です。