在PostgreSQL中返回名为"user"的表中的行数。

我有一个名为user的表。 我有一些SQL语句,如select User.* from User。 这个查询失败了,因为User是一个保留字。然而,我以为Postgresql在执行SQL时不会关心表名的大小写,所以我认为这是因为保留字造成的冲突。 在数据库中,用户表是小写的,所以如果我运行以下查询: select * from user 我会得到结果,尽管奇怪的是,我只返回了一列,尽管我说要显示所有列。 如果我运行select * from "user",我会得到所有列,而且SELECT "user".* FROM "user"也会返回所有列。 请问有人能解释一下发生了什么吗? 谢谢。 (使用PostgreSQL 9.3)

好的,从评论中我看到PG有自己的用户表。那为什么如果我执行SELECT * FROM "User"就不起作用呢? - Jon
请查看更新的答案。 - Craig Ringer
1个回答

user是一个伪函数关键字

usercurrent_user的别名,是一个内置的伪函数。它没有大多数零参数函数的(),因为SQL标准委员会规定current_user应该这样。我不知道为什么user也遵循这个规定,因为我认为规范并没有要求。

函数可以在FROM子句中使用

无论如何,在PostgreSQL中指定一个函数在from子句中是合法的,例如:

regress=> SELECT * from clock_timestamp();
        clock_timestamp        
-------------------------------
 2014-09-03 17:00:59.191019+08
(1 row)
因为一个返回单个值的函数也可以在返回集合的上下文中使用。 所以当你运行:
select * from user;
你真的做得很好:
select current_user;

"引用"转义关键字

如果你写"user"而不是"用户",它之所以“有效”,是因为带引号的标识符始终是用户标识符,而不是关键字user是一个关键字;"user"是一个合法的表名的用户定义标识符。

有点混乱,对吧?

通常情况下,"tablename"tablename是相同的(带引号的小写和无引号的小写),除非tablename是一个关键字。

如果我们选择一个不是伪函数的关键字,这可能会更有意义,因此它不是合法的语法。比如:

regress=> CREATE TABLE "where" (id integer);
CREATE TABLE

regress=> CREATE TABLE where (id integer);
ERROR:  syntax error at or near "where"
LINE 1: CREATE TABLE where (id integer);
                     ^
引用使标识符区分大小写 通常情况下,PostgreSQL将所有标识符转换为小写。这是SQL规范所要求的,尽管实际上它要求使用大写。因此,当您:
CREATE TABLE MyTable (id integer);
PostgreSQL实际上创建了一个名为mytable的表。然而,如果您使用"双引号"标识符,这种大小写折叠将被禁用 - 同样是根据SQL标准。 所以原因是:
select * from "User";

失败的原因是,根据SQL标准,PostgreSQL将带引号的标识符视为区分大小写。因此,"User""USER""user"都是不同的东西。

如何避免这些问题?

不要将表命名为关键字,例如类型名称、函数名称等。虽然这通常是合法的,但很少是一个好主意。

如果您打算使用关键字作为名称,在出现的地方始终"双引号"括起来,并且实际上应该养成这样的习惯,即始终以一致的大小写使用所有标识符。

这里有一个关键字列表


而且,“select User.* from User”不应该失败,对吧(尽管OP声称失败了)? - ypercubeᵀᴹ
@ypercube 当然可以。PostgreSQL将User转换为user,解析器将其视为current_user关键字的别名。因此,你正在编写select current_user.* from current_user这是无意义的。现在,SELECT "user".* FROM "user"就没问题了。 - Craig Ringer
1哦,对了。我本来以为它会像select * from User那样运行,但显然我需要咖啡提神。谢谢。 - ypercubeᵀᴹ
1避免这些问题的真正方法是始终给您的表名和列名加上双引号,或者只需使用一个ORM。 - beiller
很好的回答,但是把解决方案 select * from "user"; 放在最前面,然后再跟上解释,因为这个解决方案已经解释了一半:即存在某个别名和“表名”阻止了函数调用。 - Petrus Theron