【逆引き】よく使うSQLまとめ【MySQL】
よく使うMySQLのSQL構文を、逆引きできるよう日本語でまとめました!
このSQLが何をしているものなのか、わかりやすいように日本語で書いてあります。やりたいことをCtrl(command) + Fキーで検索できるよう、それっぽい語句を含ませてます。
ORマッパーを使っていると、生のSQLを書く機会はあまり多くないと思いますが、不具合調査したりちょっとデータを修正するくらいで、あればやっぱりSQLを書けた方が便利ですよね。
記事のフォーマット
- (日本語で)何をしているSQLか
- SQL
- 結果
を1セットでまとめています。
(前提条件)テーブル定義とデータ・MySQLのバージョン
この記事で使うテーブル定義とデータはこのようになっています。
ユーザーテーブルと記事テーブルで、ユーザーは記事を複数持ちます(1対多)
users
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| email | varchar(255) | NO | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
+----+--------------+-------------------+---------------------+------------+
| id | name | email | created_at | updated_at |
+----+--------------+-------------------+---------------------+------------+
| 1 | ユーザ1 | user1@example.com | 2020-08-01 00:00:00 | NULL |
| 2 | ユーザ2 | user2@example.com | 2020-09-01 00:00:00 | NULL |
| 3 | ユーザ3 | user3@example.com | 2020-10-01 00:00:00 | NULL |
+----+--------------+-------------------+---------------------+------------+
articles
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int unsigned | NO | PRI | NULL | auto_increment |
| user_id | int unsigned | NO | | NULL | |
| title | varchar(255) | NO | | NULL | |
| content | text | YES | | NULL | |
| image | varchar(255) | YES | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
+----+---------+-------------+------------+-------------+---------------------+------------+
| id | user_id | title | content | image | created_at | updated_at |
+----+---------+--------------+------------+------------+---------------------+------------+
| 1 | 1 | タイトル1 | 本文1 | NULL | 2020-08-01 00:00:00 | NULL |
| 2 | 2 | タイトル2−1 | NULL | picture.jpg | 2020-09-01 00:00:00 | NULL |
| 3 | 2 | タイトル2−2 | 本文2−2 | NULL | 2020-10-01 00:00:00 | NULL |
+----+---------+-------------+------------+-------------+---------------------+------------+
MySQLのバージョン
バージョンは「8.0.20」を使った結果になります。
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.20 |
+-----------+
逆引きSQL
●SQL ステートメントの構文
データベースの一覧を表示
show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| test_database |
+--------------------+
テーブルの一覧を表示
show tables;
+-------------------------+
| Tables_in_test_database |
+-------------------------+
| articles |
| users |
+-------------------------+
テーブルの一覧と、テーブルの種類を表示
テーブルの場合は BASE TABLE、ビューの場合は VIEW が表示されます。
show full tables;
+-------------------------+------------+
| Tables_in_test_database | Table_type |
+-------------------------+------------+
| articles | BASE TABLE |
| users | BASE TABLE |
+-------------------------+------------+
テーブルレイアウトを確認
↑で説明用テーブルの紹介で使ったテーブル定義を確認するコマンドです。フレームワークからテーブルを作成したときはこの辺り自動で設定されますが、実際のテーブルがどうなっているのか確認したいときに使うと思います。
DESC users;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| email | varchar(255) | NO | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
●データ操作言語 (DML) WHERE句
idが「1」のユーザを抽出【 = 】
対象列がINTなどの数値型の場合
SELECT * FROM users WHERE id = 1;
+----+--------------+-------------------+---------------------+------------+
| id | name | email | created_at | updated_at |
+----+--------------+-------------------+---------------------+------------+
| 1 | ユーザ1 | user1@example.com | 2020-08-01 00:00:00 | NULL |
+----+--------------+-------------------+---------------------+------------+
名前が「ユーザ2」のユーザを抽出【 = ''】
対象列がVARCHARなどの文字型の場合はシングルコーテーションで囲います。
ちなみに = でNULLのデータは抽出されません。NULLを取得したい場合は後述のIS NULLを参照
SELECT * FROM users WHERE name = 'ユーザ2';
+----+--------------+-------------------+---------------------+------------+
| id | name | email | created_at | updated_at |
+----+--------------+-------------------+---------------------+------------+
| 2 | ユーザ2 | user2@example.com | 2020-09-01 00:00:00 | NULL |
+----+--------------+-------------------+---------------------+------------+
idが1以外のユーザを抽出【 != 】
否定は != でも <> でもかけますが、個人的には != 推し
例は挙げてませんが、否定も同じく文字型の場合はシングルコーテーションが必要です。
SELECT * FROM users WHERE id != 1;
+----+--------------+-------------------+---------------------+------------+
| id | name | email | created_at | updated_at |
+----+--------------+-------------------+---------------------+------------+
| 2 | ユーザ2 | user2@example.com | 2020-09-01 00:00:00 | NULL |
| 3 | ユーザ3 | user3@example.com | 2020-10-01 00:00:00 | NULL |
+----+--------------+-------------------+---------------------+------------+
idが1,2のもの(値のリストに含まれるもの)【IN】
値のリストを持つデータを取得することができます。a OR b で置き換えることもできます。
SELECT * FROM articles WHERE id IN (1, 2);
+----+---------+--------------------+-----------+-------------+---------------------+------------+
| id | user_id | title | content | image | created_at | updated_at |
+----+---------+--------------------+-----------+-------------+---------------------+------------+
| 1 | 1 | タイトル1 | 本文1 | NULL | 2020-08-01 00:00:00 | NULL |
| 2 | 2 | タイトル2−1 | NULL | picture.jpg | 2020-09-01 00:00:00 | NULL |
+----+---------+--------------------+-----------+-------------+---------------------+------------+
idが1,2以外のもの(値のリストに含まれないもの)【NOT IN】
値のリストを持たないデータを取得することができます。NOT(a AND b)で置き換えることができます。NULLは対象外です。
SELECT * FROM articles WHERE id NOT IN (1, 2);
+----+---------+--------------------+-----------------+-------+---------------------+------------+
| id | user_id | title | content | image | created_at | updated_at |
+----+---------+--------------------+-----------------+-------+---------------------+------------+
| 3 | 2 | タイトル2−2 | 本文2−2 | NULL | 2020-10-01 00:00:00 | NULL |
+----+---------+--------------------+-----------------+-------+---------------------+------------+
イメージが登録されていない記事を抽出【IS NULL】
NULLは = や != では比較できません。NULLはNULLです。
SELECT * FROM articles WHERE image IS NULL;
+----+---------+-------------+------------+-------------+---------------------+------------+
| id | user_id | title | content | image | created_at | updated_at |
+----+---------+--------------+------------+------------+---------------------+------------+
| 1 | 1 | タイトル1 | 本文1 | NULL | 2020-08-01 00:00:00 | NULL |
| 3 | 2 | タイトル2−2 | 本文2−2 | NULL | 2020-10-01 00:00:00 | NULL |
+----+---------+-------------+------------+-------------+---------------------+------------+
イメージが登録されている記事を抽出【IS NOT NULL】
SELECT * FROM articles WHERE image IS NOT NULL;
+----+---------+-------------+------------+-------------+---------------------+------------+
| id | user_id | title | content | image | created_at | updated_at |
+----+---------+--------------+------------+------------+---------------------+------------+
| 2 | 2 | タイトル2−1 | NULL | picture.jpg | 2020-09-01 00:00:00 | NULL |
+----+---------+-------------+------------+-------------+---------------------+------------+
記事があるユーザを抽出【EXISTS】
苦手な人が多いEXISTS。
EXISTS内のサブクエリの結果が真になる(1行でも記事が存在する)ユーザが抽出されます。サブクエリのWHEREがキモです。
ちなみにサブクエリ内のアスタリスクに意味はありません。「1」でも「'zenn'」でも結果は変わりません。(EXISTS または NOT EXISTS を使用したサブクエリー)
SELECT * FROM users WHERE EXISTS(
SELECT * FROM articles WHERE users.id = articles.user_id
);
+----+--------------+-------------------+---------------------+------------+
| id | name | email | created_at | updated_at |
+----+--------------+-------------------+---------------------+------------+
| 1 | ユーザ1 | user1@example.com | 2020-08-01 00:00:00 | NULL |
| 2 | ユーザ2 | user2@example.com | 2020-09-01 00:00:00 | NULL |
+----+--------------+-------------------+---------------------+------------+
記事がないユーザを抽出【NOT EXISTS】
サブクエリ内のデータが偽になる(1行も記事が存在しない)ユーザが抽出されます。
SELECT * FROM users WHERE NOT EXISTS(
SELECT * FROM articles WHERE users.id = articles.user_id
);
+----+--------------+-------------------+---------------------+------------+
| id | name | email | created_at | updated_at |
+----+--------------+-------------------+---------------------+------------+
| 3 | ユーザ3 | user3@example.com | 2020-10-01 00:00:00 | NULL |
+----+--------------+-------------------+---------------------+------------+
●データ操作言語 (DML) SELECT句
記事数の集計【COUNT】
ただ件数を数えたいだけならGROUP BYは不要。
SELECT COUNT(*) FROM articles;
+----------+
| COUNT(*) |
+----------+
| 3 |
+----------+
ユーザ毎の合計記事数【COUNT 〜 GROUP BY】
列の値ごと(集計キーごと)に集計したい場合はGROUP BYが必要。
SELECT user_id, COUNT(*) FROM articles GROUP BY user_id;
+---------+----------+
| user_id | COUNT(*) |
+---------+----------+
| 1 | 1 |
| 2 | 2 |
+---------+----------+
作成日の順位をつける【ROW_NUMBER】
順位をつけてくれる便利なウィンドウ関数です。このSQLだけだとありがたみを感じないかもしれません・・。でもこれを覚えるとどんなデータでも取得できる気がしてきます。
RANK()との違いは、同順だった場合に順位が一意になるかならないかで、ROW_NUMBERの場合は必ず一意になり、RANKの場合はなりません(例えば1、1、3、4、5とかになる)
SELECT id, title, created_at, ROW_NUMBER() OVER (ORDER BY created_at) AS row_num
FROM articles;
+----+-----------------------+---------------------+---------+
| id | title | created_at | row_num |
+----+-----------------------+---------------------+---------+
| 1 | タイトル1 | 2020-08-01 00:00:00 | 1 |
| 2 | タイトル2−1 | 2020-09-01 00:00:00 | 2 |
| 3 | タイトル2−2 | 2020-10-01 00:00:00 | 3 |
+----+-----------------------+---------------------+---------+
作成日の順位をつけて、一番古い記事を取得【ROW_NUMBER】
先程のSQLをサブクエリにしただけですが、1つの前の応用です。順位をつけることができたなら、それを抽出条件にすることもできるということです。ベスト3とかワースト3とか、並べ替えた内の数件だけ取得したいといった場面で活躍します。
SELECT a.id, a.title, a.created_at, a.row_num FROM (
SELECT articles.*
, ROW_NUMBER() OVER (ORDER BY created_at) AS row_num
FROM articles
) a
WHERE a.row_num = 1;
+----+-----------------------+---------------------+---------+
| id | title | created_at | row_num |
+----+-----------------------+---------------------+---------+
| 1 | タイトル1 | 2020-08-01 00:00:00 | 1 |
+----+-----------------------+---------------------+---------+
●データ操作言語 (DML) 文字列編集
文字列(または列)の結合【CONCAT】
任意の文字列の場合はシングルコーテーションで囲みます。
SELECT CONCAT('タイトルは「', title, '」です') AS title FROM articles;
+-----------------------+
| title |
+-----------------------+
| タイトルは「タイトル1」です |
| タイトルは「タイトル2−1」です |
| タイトルは「タイトル2−2」です |
+-----------------------+
文字を左から切り取り【LEFT】
指定した文字数分を左から切り取ります(バイト数ではないので注意)。
SELECT title, LEFT(title, 2) AS title_mdf FROM articles;
+-----------------------+-----------+
| title | title_mdf |
+-----------------------+-----------+
| タイトル1 | タイ |
| タイトル2−1 | タイ |
| タイトル2−2 | タイ |
+-----------------------+-----------+
文字を右から切り取り【RIGHT】
指定した文字数分を右から切り取ります(バイト数ではないので注意)。
SELECT title, RIGHT(title, 2) AS title_mdf FROM articles;
+-----------------------+-----------+
| title | title_mdf |
+-----------------------+-----------+
| タイトル1 | ル1 |
| タイトル2−1 | −1 |
| タイトル2−2 | −2 |
+-----------------------+-----------+
指定位置から指定文字数を切り取り【SUBSTR】
SUBSTRは、SUBSTRINGのシノニムです(別名のこと。機能は同じ)。位置は何文字目かを指定します。
- 指定位置から最後まで
SELECT title, SUBSTR(title, 2) AS title_mdf FROM articles;
+-----------------------+--------------------+
| title | title_mdf |
+-----------------------+--------------------+
| タイトル1 | イトル1 |
| タイトル2−1 | イトル2−1 |
| タイトル2−2 | イトル2−2 |
+-----------------------+--------------------+
- 指定位置から指定文字数まで:SUBSTR(title, 2, 4)
位置にマイナスの数値を指定すると、「末尾から何文字目か」になります
SELECT title, SUBSTR(title, 2, 4) AS title_mdf FROM articles;
+-----------------------+--------------+
| title | title_mdf |
+-----------------------+--------------+
| タイトル1 | イトル1 |
| タイトル2−1 | イトル2 |
| タイトル2−2 | イトル2 |
+-----------------------+--------------+
文字列⇒日付、日付⇒文字列、置き換え
未
●データ操作言語 (DML) JOIN句
未
●データ操作言語 (DML) ORDER BY句
未
まとめ
以上、MySQLの逆引きSQLでした!思い出したら追加していきます。
不明なところや、この場合はどうしたらえぇねんとかあれば、コメントかTwitterへ(@paya02_ictdev)
DBの用意にはこちらの記事を参考にさせていただきました!ありがとうございます。
docker-compose でMySQL環境簡単構築
Discussion