为什么这个子选择工作?没有FROM条款

时间:2018-01-11 13:28:51

标签: sql postgresql sql-update

我在编写PostgreSQL请求时犯了一个愚蠢的错误。语法似乎完全错误,但它仍然有一半工作。

我有这个表(订阅者):

id - device_id - invert
1    'abcd'       true
2    'abcd'       true

还有其他行,但这些行是唯一关注的行(具有相同的device_id

我提出了请求:

UPDATE subscriber 
   SET invert = false 
WHERE device_id = (SELECT device_id WHERE id = 1) 
RETURNING *;

我忘记了子选择的FROM子句。按预期(device_id does not exist)对其自身崩溃进行子选择。但是,完整请求计算,并确实更新并返回行中的一行(id = 1)。正确的请求应该更新并返回两行。

为什么呢?这是某种虫子吗?或者子选择与选择的行为有何不同?

2 个答案:

答案 0 :(得分:1)

我认为以前曾提出类似的问题,但我现在找不到它们。

在子选择内部,外部查询中的所有内容也是可见的。如果在子选择中存在名称冲突“本地”标识符,则优先于“外部”标识符。如果标识符在子选择中无效,但在外部查询中可用,则使用外部查询中的标识符。

Postgres允许没有FROM子句的SELECT语句,所以

select 42;

有效

因此,您的子选择实际上使用了正在更新的表中的device_idid列。这就是查询有效的原因。

这不是一个错误,SQL标准需要这些可见性规则(但是,在没有SELECT的情况下运行FROM的可能性是SQL标准的扩展。)

答案 1 :(得分:1)

我很害怕,跳过FROM你在括号中创建了一个相关的子查询,而不仅仅是一个子查询,例如:

t=# select oid,(select oid) from pg_database limit 2;
  oid  |  oid
-------+-------
     1 |     1
 12945 | 12945
(2 rows)

这样Postgres会尝试猜测用于列名的别名,在我的情况下是pg_database ...所以在你的情况下你的where device_id = (SELECT device_id WHERE id = 1)在逻辑上等于where id = 1 ...

另一个例子:

t=# select oid,(select oid::int*2 where oid=1),2 from pg_database limit 2;
  oid  | ?column? | ?column?
-------+----------+----------
     1 |        2 |        2
 12945 |          |        2
(2 rows)