我在编写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
)。正确的请求应该更新并返回两行。
为什么呢?这是某种虫子吗?或者子选择与选择的行为有何不同?
答案 0 :(得分:1)
我认为以前曾提出类似的问题,但我现在找不到它们。
在子选择内部,外部查询中的所有内容也是可见的。如果在子选择中存在名称冲突“本地”标识符,则优先于“外部”标识符。如果标识符在子选择中无效,但在外部查询中可用,则使用外部查询中的标识符。
Postgres允许没有FROM
子句的SELECT语句,所以
select 42;
有效
因此,您的子选择实际上使用了正在更新的表中的device_id
和id
列。这就是查询有效的原因。
这不是一个错误,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)