LoginSignup
6
2

More than 3 years have passed since last update.

PostgreSQLのUPDATE文の仕様を腹落ちさせる

Last updated at Posted at 2020-09-16

ことの発端

PostgreSQL 9.6環境で、下記UPDATE文を実行したところ、

UPDATE
  table1
SET 
  table1.column1 = table2.column1
FROM
  table2
WHERE
  table2.table1_id = table1.id
;

下記エラーが発生した。

Error: ERROR: column "table1" of relation "table1" does not exist

なぜ?

公式リファレンスでSET句の左辺の仕様を読んだところ

table_nameで指名されたテーブル内の列名です。 必要に応じて、列名を副フィールド名や配列の指示子で修飾することも可能です。
対象列の指定にはテーブル名を含めないでください。 たとえば、UPDATE table_name SET table_name.col = 1は無効です。

と書かれており、エイリアスの使用が禁止されていることがわかった。
なるほど、と思いつつ、

  • 別にエイリアスを使用したところで処理には影響ないのでは?
  • なぜSET句の左辺だけエイリアスの使用が禁止されているのか?

という疑問が残ったので、腹落ちするまで調べてみた。

結論

  • エイリアスを使用できる = 任意のテーブル内のカラムを参照できる
  • エイリアスを使用できない = 特定のテーブル内のカラムしか参照できない

という解釈で腹落ち。

PostgreSQLのUPDATE文は 同時に複数テーブルを更新できない という制約があるため、SET句の左辺に記述できるカラムは、必然的にUPDATE句で指定したテーブル内のカラムになる。
つまり、特定のテーブル内のカラムしか参照できないため、エイリアスを使用できない制約にしている(のではないか)。

おまけ

MySQLやSQL Serverでも同じ仕様なのか気になり、SET句の左辺でエイリアスを使用してUPDATE文を実行したところ、 正常に更新された。

MySQL 5.7の場合

UPDATE
  table1, table2
SET 
  table1.column1 = table2.column1
WHERE 
  table2.table1_id = table1.id
;

SQL Server 2017 Standardの場合

UPDATE
  table1
SET 
  table1.column1 = table2.column1
FROM 
  table2
WHERE 
  table2.table1_id = table1.id
;

MySQLの場合、UPDATE句に複数テーブルを記述することができるので、「そりゃできるよな」という所感だが、SQL ServerはPostgreSQLと同じUPDATE文なため、少しばかり違和感を覚える。

普段SELECT文を書く際にテーブル名.カラム名で記述しているため、SQL Serverの仕様の方が(個人的には)しっくりくる。
ただ、そもそもUPDATE句で指定したテーブル内のカラムしか更新できないのであれば、 エイリアスの使用を禁止した方が、他テーブルを参照できてしまうという曖昧さを潰せる というメリットもあるなぁ、という気づきも今回の調査で得ることができた。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2