- SQL
SQL ASで別名をつける使い方 WHEREで別名が使えない理由と対処
SQL の AS は、列やテーブルに別名(エイリアス)をつけるキーワードで、SELECT price * qty AS total のように「式 AS 別名」の形で書きます。 つけた列別名は結果の見出しになり、ORDER BY ではそのまま使えます。一方、WHERE では使えません。WHERE は SELECT より先に評価されるため、その時点ではまだ別名が存在しないからです。
この記事では、WHERE・GROUP BY・HAVING・ORDER BY のそれぞれで列別名が使えるかを DB 別の早見表にまとめ、MySQL と PostgreSQL の実際のエラーメッセージを再現します。そのうえで、評価順序の図で理由を説明し、式の書き直し・サブクエリ・CTE の3つの対処をコピペできる形で示します。AS を省略できるかどうか、日本語や空白入りの別名を囲む引用符の違いも表にしました。SQL は MySQL 8.0.46・PostgreSQL 16.15・SQLite 3.45.1 で実行しています。Oracle と SQL Server は手元で動かしていないため、公式ドキュメントで確かめた範囲と「未実行」の範囲を分けて書いています。
列別名が使える句の早見表
SELECT でつけた列別名を、ほかの句で参照できるかどうかの一覧です。
| 句 | MySQL 8.0 | PostgreSQL 16 | SQLite 3.45 | Oracle | SQL Server |
|---|---|---|---|---|---|
| WHERE | 使えない | 使えない | 使える | 使えない | 使えない |
| GROUP BY | 使える | 使える | 使える | 23 から使える(それ以前は使えない) | 使えない |
| HAVING | 使える | 使えない | 使える | 式で書くのが確実 | 使えない |
| ORDER BY(別名だけ) | 使える | 使える | 使える | 使える | 使える |
| ORDER BY(別名を含む式) | 使える | 使えない | 使える | 未確認 | 未確認 |
| 同じ SELECT 内のほかの列 | 使えない | 使えない | 使えない | 未確認 | 未確認 |
| 確認方法 | 実行 | 実行 | 実行 | 公式ドキュメント(未実行) | 公式ドキュメント(未実行) |
- MySQL・PostgreSQL・SQLite の列は、この記事のサンプルで実際に実行した結果です。
- Oracle は、SQL 言語リファレンス(23)の SELECT のページに「列別名は ORDER BY 句で使えるが、問合せのほかの句では使えない」「リリース 23 から、位置と別名でグループ化できる」という趣旨の記載があることを確認しました。HAVING での別名については今回確認できていないため、式で書く形をおすすめします。
- SQL Server は、公式ドキュメントの SELECT のページに「SELECT 句で定義した列別名は、それより前の句からは参照できないが、ORDER BY など後の句からは参照できる」という趣旨の記載があることを確認しました。
AS の書き方の早見表も置いておきます。
| つける対象 | 書き方 | AS の省略 |
|---|---|---|
| 列 | SELECT price * qty AS total |
省略できる(書くのがおすすめ) |
| テーブル | FROM order_items AS oi |
省略できる。Oracle は AS を書けない |
| サブクエリ | FROM (SELECT ...) AS t |
省略できる。MySQL は別名そのものが必須 |
| 日本語の別名 | SELECT item AS 商品名 |
引用符なしで書ける(実行した3つの DB で確認) |
| 空白入りの別名 | SELECT item AS "商品 名" |
引用符で囲む。使える引用符は DB で違う |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「SQL ASで別名をつける使い方 WHEREで別名が使えない理由と対処」
サンプルテーブルを用意する
注文明細のテーブルを使います。MySQL・PostgreSQL・SQLite のどれでもそのまま動きます。
CREATE TABLE order_items (
id INT PRIMARY KEY,
category VARCHAR(20) NOT NULL,
item VARCHAR(20) NOT NULL,
price INT NOT NULL,
qty INT NOT NULL
);
INSERT INTO order_items VALUES
(1, '文具', 'ノート', 300, 5),
(2, '文具', 'ペン', 150, 2),
(3, '家具', '椅子', 8000, 1),
(4, '家具', '机', 12000, 2),
(5, '雑貨', '時計', 2500, 1);
ASで列とテーブルに別名をつける基本
SELECT item AS 商品名, price * qty AS total
FROM order_items oi
ORDER BY id;
実行結果(MySQL 8.0.46):
+-----------+-------+
| 商品名 | total |
+-----------+-------+
| ノート | 1500 |
| ペン | 300 |
| 椅子 | 8000 |
| 机 | 24000 |
| 時計 | 2500 |
+-----------+-------+
この1文に、別名の基本が3つ入っています。
| 部分 | 種類 | 役割 |
|---|---|---|
item AS 商品名 |
列別名 | 結果の見出しを読みやすい名前にする |
price * qty AS total |
列別名(式) | 計算結果に名前をつける。つけないと見出しが式そのものになる |
order_items oi |
テーブル別名(AS 省略) | 長いテーブル名を短く書く。結合や自己結合で列の出どころを示す |
別名をつけても、テーブルの定義やデータは変わりません。別名が有効なのは、その SQL 文の中だけです。
WHEREで別名を使うとエラーになる例
計算した total が 5,000 以上の行だけ欲しいとき、次のように書きたくなります。
SELECT item, price * qty AS total
FROM order_items
WHERE total >= 5000;
MySQL 8.0.46:
ERROR 1054 (42S22) at line 1: Unknown column 'total' in 'where clause'
PostgreSQL 16.15:
ERROR: column "total" does not exist
LINE 1: ...item, price * qty AS total FROM order_items WHERE total >= 5...
^
SQLite 3.45.1 は、この SQL をエラーにせず実行しました。
item total
---- -----
椅子 8000
机 24000
DB ごとのメッセージをまとめます。
| DB | メッセージ | 確認 |
|---|---|---|
| MySQL | ERROR 1054 (42S22): Unknown column 'total' in 'where clause' |
8.0.46 で実行 |
| PostgreSQL | ERROR: column "total" does not exist |
16.15 で実行 |
| SQLite | エラーにならず、別名で絞り込める | 3.45.1 で実行 |
| Oracle | ORA-00904: "TOTAL": invalid identifier(無効な識別子) |
未実行(一般に知られたメッセージ) |
| SQL Server | Msg 207 ... Invalid column name 'total'.(列名が無効) |
未実行(一般に知られたメッセージ) |
どのメッセージも「total という列は見つからない」と言っています。テーブルに total という列はなく、別名もまだ存在しない、というのが DB 側から見た状況です。SQLite で動くのは SQLite 独自の拡張で、ほかの DB に持っていくとエラーになります。複数の DB で使う SQL は、後述の3つの対処のいずれかで書いておくと移しやすくなります。
WHEREで別名が使えない理由は評価順序
SELECT 文は SELECT から書き始めますが、DB が意味を解釈する順番は書いた順番と違います。
flowchart LR
A["1. FROM / JOIN<br>対象の表を決める"] --> B["2. WHERE<br>行を絞る"]
B --> C["3. GROUP BY<br>グループにまとめる"]
C --> D["4. HAVING<br>グループを絞る"]
D --> E["5. SELECT<br>列を計算し別名がつく"]
E --> F["6. ORDER BY<br>並べ替える"]
F --> G["7. LIMIT など<br>件数を絞る"]
| 順番 | 句 | その時点で列別名は存在するか |
|---|---|---|
| 1 | FROM / JOIN | まだない(テーブル別名はここでつく) |
| 2 | WHERE | まだない |
| 3 | GROUP BY | まだない |
| 4 | HAVING | まだない |
| 5 | SELECT | ここで列別名がつく |
| 6 | ORDER BY | ある |
| 7 | LIMIT など | ある |
列別名がつくのは5番目の SELECT です。WHERE は2番目なので、まだ別名が生まれていません。 ORDER BY は SELECT より後なので、別名を使えます。
この順番は SQL Server の公式ドキュメントに「SELECT ステートメントの論理的な処理順序」として FROM、ON、JOIN、WHERE、GROUP BY、HAVING、SELECT、DISTINCT、ORDER BY、TOP の順で載っています。MySQL の公式ドキュメント(カラムエイリアスに関する問題のページ)にも、「標準 SQL では WHERE 句でのカラムエイリアスへの参照は許可されない。WHERE 句が評価されるときに、カラム値がまだ判別されていない場合があるため」という趣旨の説明があります。
テーブル別名は1番目の FROM でつくので、WHERE でも SELECT でも使えます。「テーブル別名はどこでも使えるのに、列別名は WHERE で使えない」のは、つくタイミングが違うためです。
図の順番どおりなら GROUP BY と HAVING でも列別名は使えないはずですが、早見表のとおり MySQL や PostgreSQL は一部を独自に受け付けています。次の章で実際の結果を見ていきます。
GROUP BY・HAVING・ORDER BYでの別名をDB別に確かめる
GROUP BYでの別名
SELECT CASE WHEN price >= 1000 THEN 'high' ELSE 'low' END AS band,
COUNT(*) AS cnt
FROM order_items
GROUP BY band
ORDER BY band;
MySQL 8.0.46・PostgreSQL 16.15・SQLite 3.45.1 のいずれでも、次の結果になりました(表示は PostgreSQL のもの)。
band | cnt
------+-----
high | 3
low | 2
(2 rows)
長い CASE 式を GROUP BY に繰り返し書かずに済むので、この3つの DB では便利な書き方です。Oracle は 23 より前の版、SQL Server では別名を受け付けないため、GROUP BY CASE WHEN price >= 1000 THEN 'high' ELSE 'low' END と式をそのまま書くか、後述のサブクエリや CTE を使います(Oracle と SQL Server は未実行)。
HAVINGでの別名
SELECT category, SUM(price * qty) AS total
FROM order_items
GROUP BY category
HAVING total >= 5000
ORDER BY category;
MySQL 8.0.46(SQLite 3.45.1 も同じ1行を返しました):
+----------+-------+
| category | total |
+----------+-------+
| 家具 | 32000 |
+----------+-------+
PostgreSQL 16.15:
ERROR: column "total" does not exist
LINE 1: ...S total FROM order_items GROUP BY category HAVING total >= 5...
^
MySQL では HAVING で別名が使え、PostgreSQL では使えません。 PostgreSQL の公式ドキュメント(SELECT のページ)にも「出力列の名前は ORDER BY と GROUP BY では使えるが、WHERE と HAVING では使えない。そこでは式を書く必要がある」という趣旨の記載があります。どの DB でも動く形は、集計関数をそのまま書く方法です。
SELECT category, SUM(price * qty) AS total
FROM order_items
GROUP BY category
HAVING SUM(price * qty) >= 5000
ORDER BY category;
この形は MySQL・PostgreSQL・SQLite の3つで同じ結果(家具 32000)になることを確認しました。
ORDER BYでの別名
SELECT item, price * qty AS total
FROM order_items
ORDER BY total DESC;
item | total
--------+-------
机 | 24000
椅子 | 8000
時計 | 2500
ノート | 1500
ペン | 300
(5 rows)
別名だけを書く形は、実行した3つの DB すべてで使えました。ただし、別名を式の中に入れると PostgreSQL ではエラーになります。
SELECT item, price * qty AS total
FROM order_items
ORDER BY total * -1;
ERROR: column "total" does not exist
LINE 1: ...m, price * qty AS total FROM order_items ORDER BY total * -1...
^
MySQL と SQLite ではこの SQL も実行でき、total の大きい順に並びました。PostgreSQL で同じことをするなら、ORDER BY price * qty * -1 と元の式で書くか、ORDER BY total DESC のように別名だけで表現します。
同じSELECTの中で別名を再利用する
SELECT price * qty AS total, total * 1.1 AS with_tax
FROM order_items;
| DB | 結果 |
|---|---|
| MySQL 8.0.46 | ERROR 1054 (42S22): Unknown column 'total' in 'field list' |
| PostgreSQL 16.15 | ERROR: column "total" does not exist |
| SQLite 3.45.1 | no such column: total |
同じ SELECT リストの中では、どの DB でも別名を参照できませんでした。 WHERE では別名を受け付ける SQLite も、ここではエラーになります。計算結果をさらに計算に使いたいときも、次の章の対処が使えます。
WHEREで別名を使いたいときの3つの対処
対処1:式をもう一度書く
もっとも単純な方法です。WHERE に、別名ではなく元の式を書きます。
SELECT item, price * qty AS total
FROM order_items
WHERE price * qty >= 5000
ORDER BY total DESC;
対処2:サブクエリで包む
別名をつけた SELECT を FROM の中に入れ、外側の WHERE で別名を使います。
SELECT item, total
FROM (
SELECT item, price * qty AS total
FROM order_items
) AS t
WHERE total >= 5000
ORDER BY total DESC;
対処3:CTE(WITH句)で名前をつける
サブクエリを先頭に取り出して名前をつける書き方です。
WITH t AS (
SELECT item, price * qty AS total
FROM order_items
)
SELECT item, total
FROM t
WHERE total >= 5000
ORDER BY total DESC;
3つの対処は、MySQL 8.0.46・PostgreSQL 16.15・SQLite 3.45.1 のすべてで同じ結果になりました。
item | total
------+-------
机 | 24000
椅子 | 8000
(2 rows)
サブクエリと CTE で別名が使えるのは、評価順序で説明できます。内側の SELECT が先に評価されて total という列を持つ表ができあがり、外側の FROM はその表を受け取ります。外側から見ると、total は別名ではなく「最初からある列」 なので、WHERE でも GROUP BY でも自由に使えます。
3つの対処の選び方
| 対処 | 向いている場面 | 気をつける点 |
|---|---|---|
| 1. 式を書き直す | 式が短い。1か所でしか使わない | 式を直すときに、SELECT と WHERE の両方を直す必要がある |
| 2. サブクエリ | 式が長い。同じ計算結果を何か所かで使う | 入れ子が深くなると読みにくい |
| 3. CTE | 段階を追って計算したい。同じ結果を複数回参照する | MySQL は 8.0 以降で対応 |
式が長くなるほど、同じ式を2か所に書く対処1は直し漏れが起きやすくなります。計算は1か所にまとめ、名前で参照するという意味で、対処2と3は別名本来の良さを活かせる書き方です。
ウィンドウ関数の結果で絞りたいとき
ROW_NUMBER などのウィンドウ関数は WHERE に直接書けないので、対処1が使えません。サブクエリか CTE で包みます。
-- エラーになる例
SELECT item, ROW_NUMBER() OVER (ORDER BY price DESC) AS rn
FROM order_items
WHERE rn <= 2;
MySQL では Unknown column 'rn' in 'where clause'、PostgreSQL では column "rn" does not exist、SQLite では misuse of aliased window function rn となりました。
-- サブクエリで包む
SELECT item, rn
FROM (
SELECT item, ROW_NUMBER() OVER (ORDER BY price DESC) AS rn
FROM order_items
) t
WHERE rn <= 2;
item | rn
------+----
机 | 1
椅子 | 2
(2 rows)
応用:LATERALで同じSELECT内に名前を作る
PostgreSQL と MySQL 8.0 では、LATERAL を使うと「計算結果に名前をつけ、同じ階層の SELECT や WHERE で使う」ことができます。
SELECT oi.item, v.total, v.total * 1.1 AS with_tax
FROM order_items oi
CROSS JOIN LATERAL (SELECT oi.price * oi.qty AS total) v
WHERE v.total >= 5000
ORDER BY v.total DESC;
実行結果(PostgreSQL 16.15。MySQL 8.0.46 でも同じ2行):
item | total | with_tax
------+-------+----------
机 | 24000 | 26400.0
椅子 | 8000 | 8800.0
(2 rows)
v.total は FROM の段階で作られるので、WHERE でも SELECT でも参照できます。計算を何段も重ねたいときに、入れ子を増やさずに済む書き方です。
別名と元の列名が同じときの解釈
別名に、テーブルにもともとある列名と同じ名前をつけると、句によって「どちらを指すか」が変わります。
WHEREは元の列を見る
SELECT price * 2 AS price
FROM order_items
WHERE price >= 5000;
price
-------
16000
24000
(2 rows)
WHERE の price は元の列(8000 と 12000)で判定され、結果には2倍した値が出ています。3つの DB で同じ結果でした。
ORDER BYは別名を見る
SELECT item, -price AS price
FROM order_items
ORDER BY price;
item | price
--------+--------
机 | -12000
椅子 | -8000
時計 | -2500
ノート | -300
ペン | -150
(5 rows)
ORDER BY の price は別名(符号を反転した値)として解釈され、-12000 が先頭になりました。こちらも3つの DB で同じです。
GROUP BYは元の列を見る
SELECT CASE WHEN price >= 1000 THEN 'high' ELSE 'low' END AS category,
COUNT(*) AS cnt
FROM order_items
GROUP BY category;
別名の category でまとめたつもりでも、テーブルに category 列があるため、GROUP BY は元の列として解釈されました。
| DB | 結果 |
|---|---|
| MySQL 8.0.46 | ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 't228.order_items.price' ... |
| PostgreSQL 16.15 | ERROR: column "order_items.price" must appear in the GROUP BY clause or be used in an aggregate function |
| SQLite 3.45.1 | エラーにならず、元の category(文具・家具・雑貨)ごとの3行が返る |
PostgreSQL の公式ドキュメントにも「あいまいな場合、GROUP BY の名前は出力列名ではなく入力列名として解釈される。ORDER BY は逆に出力列名として解釈する」という趣旨の記載があります。
| 句 | 同名のときに指すもの |
|---|---|
| WHERE | 元の列 |
| GROUP BY | 元の列 |
| ORDER BY | 別名 |
別名には、テーブルにある列名と重ならない名前をつけるのがいちばんの対策です。上の例なら AS price_band のように別の名前にすれば、意図どおり別名でまとめられます。
ASを省略できるか
列別名のAS
列別名の AS は、実行した3つの DB すべてで省略できました。
SELECT item 商品, price * qty total
FROM order_items oi
WHERE oi.id = 1;
商品 | total
--------+-------
ノート | 1500
(1 row)
ただし、AS を省略する書き方は、カンマの書き忘れと見分けがつきません。
SELECT item price, qty
FROM order_items
WHERE id = 1;
price | qty
--------+-----
ノート | 5
(1 row)
item と price の間のカンマを忘れた SQL ですが、エラーにならず「item に price という別名をつけた」と解釈されました。price の値は取れておらず、見出しだけが price になっています。列別名には AS を書くと決めておくと、この取り違えに気づきやすくなります。PostgreSQL の公式ドキュメントも、将来のキーワード追加との衝突を避けるために AS を書くか、出力列名を二重引用符で囲むことを推奨しています。
予約語を別名にした場合の結果も DB で分かれました。SELECT item AS order は PostgreSQL 16.15 では実行でき、MySQL 8.0.46 と SQLite 3.45.1 では構文エラーになりました。予約語と重なる名前は避けるか、引用符で囲みます。
テーブル別名のAS
| DB | FROM order_items AS oi |
FROM order_items oi |
確認 |
|---|---|---|---|
| MySQL | 書ける | 書ける | 8.0.46 で実行 |
| PostgreSQL | 書ける | 書ける | 16.15 で実行 |
| SQLite | 書ける | 書ける | 3.45.1 で実行 |
| SQL Server | 書ける | 書ける | 未実行(一般に知られた仕様) |
| Oracle | 書けない(構文エラー) | 書ける | 未実行(一般に知られた仕様) |
Oracle はテーブル別名に AS を書けず、FROM order_items oi の形で書きます(列別名の AS は Oracle でも書けます)。複数の DB で同じ SQL を使う可能性があるなら、列別名は AS あり、テーブル別名は AS なしで統一しておくと、どの DB にも持っていけます。
テーブル別名をつけたら元の名前は使えない
SELECT order_items.item
FROM order_items AS oi
WHERE oi.id = 1;
| DB | メッセージ |
|---|---|
| MySQL 8.0.46 | ERROR 1054 (42S22): Unknown column 'order_items.item' in 'field list' |
| PostgreSQL 16.15 | ERROR: invalid reference to FROM-clause entry for table "order_items"、HINT: Perhaps you meant to reference the table alias "oi". |
| SQLite 3.45.1 | no such column: order_items.item |
別名をつけたテーブルは、その SQL の中では別名でしか参照できません。PostgreSQL はヒントで「別名 oi を参照するつもりでは」と教えてくれます。
テーブル別名が欠かせない場面:同じテーブルを2回使う
テーブル別名は「短く書くための道具」であるだけでなく、同じテーブルを1つの SQL で2回使うときには必須になります。同じカテゴリの商品同士を組にする例です。
SELECT a.item AS item, b.item AS same_category_item
FROM order_items a
JOIN order_items b ON a.category = b.category AND a.id < b.id
ORDER BY a.id;
item | same_category_item
--------+--------------------
ノート | ペン
椅子 | 机
(2 rows)
MySQL・PostgreSQL・SQLite の3つで同じ2行になりました。a と b という別名があるので、どちらの order_items の列かを書き分けられます。別名をつけずに FROM order_items JOIN order_items ON category = category と書くと、次のように止まります。
| DB | メッセージ |
|---|---|
| MySQL 8.0.46 | ERROR 1066 (42000): Not unique table/alias: 'order_items' |
| PostgreSQL 16.15 | ERROR: table name "order_items" specified more than once |
列別名も同じ理由で役に立ちます。上の例では両方の列が item なので、AS same_category_item と名前を分けておくと、結果を受け取る側が2つの列を区別できます。
サブクエリの別名
FROM に書いたサブクエリ(導出テーブル)に別名をつけなかった場合の結果です。
SELECT item FROM (SELECT item FROM order_items WHERE id = 1);
| DB | 結果 |
|---|---|
| MySQL 8.0.46 | ERROR 1248 (42000): Every derived table must have its own alias |
| PostgreSQL 16.15 | 実行できる |
| SQLite 3.45.1 | 実行できる |
MySQL ではサブクエリの別名が必須です。どの DB でも動くように、FROM のサブクエリには必ず別名をつけるのがおすすめです。
日本語や空白入りの別名と引用符の違い
日本語の別名は、空白や記号を含まなければ引用符なしで書けました(3つの DB で実行)。空白を含む場合は引用符で囲みます。どの引用符が使えるかは DB によって違います。
| 書き方 | MySQL 8.0.46 | PostgreSQL 16.15 | SQLite 3.45.1 |
|---|---|---|---|
AS 商品名(引用符なし) |
使える | 使える | 使える |
AS 商品 名(空白入り・引用符なし) |
構文エラー | 構文エラー | 構文エラー |
AS "商品 名"(二重引用符) |
使える | 使える | 使える |
AS '商品 名'(一重引用符) |
使える | 構文エラー | 使える |
| AS のあとをバッククォートで囲む | 使える | 構文エラー | 使える |
AS [商品 名](角かっこ) |
構文エラー | 構文エラー | 使える |
Oracle は二重引用符、SQL Server は二重引用符と角かっこが使えるとされています(どちらも未実行、一般に知られた仕様)。二重引用符は、実行した3つの DB すべてで通りました。 MySQL では、sql_mode に ANSI_QUOTES を加えた状態でも二重引用符の別名が使えることを確認しています。迷ったら二重引用符を選ぶと、DB をまたいでも書き換えが少なくて済みます。
一重引用符の別名はORDER BYで文字列になる
MySQL では一重引用符でも別名をつけられますが、その別名を ORDER BY に一重引用符つきで書くと、列ではなくただの文字列として扱われます。
-- 並べ替えが効かない例('nm' は文字列の定数)
SELECT item AS 'nm' FROM order_items ORDER BY 'nm' DESC LIMIT 2;
+-----------+
| nm |
+-----------+
| ノート |
| ペン |
+-----------+
-- 別名として並べ替える
SELECT item AS nm FROM order_items ORDER BY nm DESC LIMIT 2;
+--------+
| nm |
+--------+
| 椅子 |
| 机 |
+--------+
前者はエラーにならないまま、並べ替えが行われていません。一重引用符は文字列の値を表す記号なので、別名には使わないほうが取り違えを防げます。
PostgreSQLは引用符なしの別名を小文字にする
SELECT item AS ItemName FROM order_items WHERE id = 1;
| DB | 見出し |
|---|---|
| MySQL 8.0.46 | ItemName |
| PostgreSQL 16.15 | itemname |
| SQLite 3.45.1 | ItemName |
PostgreSQL は、引用符で囲まない名前を小文字にそろえます。大文字を残したいときは AS "ItemName" と二重引用符で囲みます。ただし、囲んだ別名は参照するときも同じ表記で囲む必要があり、SELECT item AS "ItemName" ... ORDER BY ItemName は column "itemname" does not exist というエラーになりました。Oracle は逆に、引用符なしの名前を大文字にそろえることで知られています(未実行)。アプリ側で列名を文字列として受け取る場合は、小文字とアンダースコアだけの別名(item_name など)にしておくと、DB による差が出にくくなります。
別名のつけ方チェックリスト
| 確認すること | おすすめの書き方 |
|---|---|
| 列別名に AS を書いているか | 書く(カンマ忘れとの見分けがつく) |
| テーブル別名は短く、意味がわかるか | order_items oi のように頭文字を使う |
| 別名が元の列名と重なっていないか | 重ならない名前にする(price_band など) |
| 別名が予約語と重なっていないか | 避ける。必要なら二重引用符で囲む |
| 空白や記号を含んでいないか | 英小文字とアンダースコアにする。必要なら二重引用符 |
| WHERE で別名を使っていないか | 式を書き直す、サブクエリ、CTE のいずれかにする |
| HAVING で別名を使っていないか | 集計関数をそのまま書く(PostgreSQL などでも動く) |
| FROM のサブクエリに別名があるか | 必ずつける(MySQL で必須) |
よくある質問
Q. AS は書いたほうがよいですか、省略したほうがよいですか? A. 列別名には書くのがおすすめです。省略すると、カンマを書き忘れたときに「前の列に次の列名を別名としてつけた」と解釈され、エラーにならないまま結果が変わります。テーブル別名は、Oracle で AS が書けないため、省略する形にそろえると DB をまたいで使えます。
Q. MySQL では HAVING で別名が使えるなら、WHERE の代わりに HAVING で絞ればよいですか?
A. MySQL 8.0.46 では、GROUP BY のない SELECT item, price * qty AS total FROM order_items HAVING total >= 5000 が実行でき、2行が返りました。ただし、これは MySQL 独自の動きで、PostgreSQL では同じ書き方ができません。また HAVING は本来、グループ化した後の絞り込みに使う句です。行の絞り込みは WHERE に式を書くか、サブクエリや CTE で包む形にしておくと、ほかの DB でも同じ SQL が使えます。
Q. サブクエリや CTE で包むと遅くなりませんか? A. 今回の記事では速度の計測はしていません。一般には、単純なサブクエリや CTE は DB が内側の条件とまとめて処理できる場合が多いとされていますが、DB の種類や版、SQL の形によって変わります。気になる場合は、EXPLAIN で実行計画を見比べて判断するのが確実です。
Q. GROUP BY 1 や ORDER BY 2 のような番号指定は別名の代わりになりますか?
A. SELECT category, COUNT(*) AS cnt FROM order_items GROUP BY 1 ORDER BY 2 DESC, 1 は、MySQL・PostgreSQL・SQLite の3つで実行できました。短く書けますが、SELECT の列の順番を変えると意味が変わるので、長く使う SQL では列名や別名で書くほうが読み手に伝わりやすくなります。
まとめ
- AS は列やテーブルに別名をつけるキーワード。別名が有効なのは、その SQL 文の中だけ
- WHERE で列別名が使えないのは評価順序のため。WHERE(2番目)は SELECT(5番目)より先に評価され、別名がまだ存在しない
- GROUP BY と HAVING は DB で分かれる。MySQL は両方で使え、PostgreSQL は GROUP BY だけ、SQL Server はどちらも使えない。ORDER BY はどの DB でも使える
- 対処は3つ:式を書き直す、サブクエリで包む、CTE で名前をつける。式が長いときはサブクエリか CTE が直しやすい
- 別名と元の列名が同じだと、WHERE と GROUP BY は元の列、ORDER BY は別名を指す。重ならない名前をつける
- 列別名は AS あり、テーブル別名は AS なしにすると、Oracle を含めて同じ書き方で通せる
- 空白入りの別名は二重引用符で囲む。一重引用符は文字列と取り違えやすい
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「SQL ASで別名をつける使い方 WHEREで別名が使えない理由と対処」
株式会社RJCでは、SQLの別名のような基礎を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。