被遗忘的赋值运算符“=”和司空见惯的“:=”

The forgotten assignment operator "=" and the commonplace ":="

提问人:A.H. 提问时间:9/18/2011 最后编辑:moodymudskipperA.H. 更新时间:5/21/2022 访问量:14129

问:

PL/pgSQL 的文档说,变量的声明和赋值是用 . 但是一个简单、更短、更现代(见脚注)似乎按预期工作::==

    CREATE OR REPLACE FUNCTION foo() RETURNS int AS $$
    DECLARE
      i int;
    BEGIN
      i = 0;  
      WHILE NOT i = 25 LOOP
          i = i + 1;
          i = i * i;
      END LOOP;
      RETURN i;
    END;
    $$ LANGUAGE plpgsql;

    > SELECT foo();
    25

请注意,Pl/pgSQL 可以清楚地区分赋值和比较,如行所示

      WHILE NOT i = 25 LOOP

所以,问题是:

  • 我没有在文档中找到一些提到和/或解释这一点的部分吗?
  • 是否有任何已知的后果使用代替?=:=

编辑/脚注:

请眨眨眼,“更现代”的部分,就像在《编程语言的简短、不完整和大多错误的历史》中一样:

1970 年 - Niklaus Wirth 创建了 Pascal,一种程序语言。评论家 立即谴责 Pascal,因为它使用了 “x := x + y” 语法 而不是更熟悉的类似 C 的“x = x + y”。这种批评 尽管 C 尚未被发明,但还是发生了。

1972年 - 丹尼斯·里奇(Dennis Ritchie)发明了一种强大的枪,可以向前射击 和向后同时。对死亡人数不满意 以及他发明的 C 和 Unix 的发明造成的永久残缺。

PostgreSQL PLPGSQL 赋值运算符 冒号等于

评论

2赞 9/19/2011
它确实很奇怪。您可能希望将其发布到 PG 邮件列表,以便 PG 开发人员可以对此发表一些意见。
0赞 Keith Thompson 9/19/2011
使用而不是使用有什么好处吗?“更现代”对我来说并不是一个优势。=:=
0赞 A.H. 9/19/2011
通常我想同意。但是,最后一种计算机语言是什么时候发明的,a)被广泛使用一半,b)使用“:=”进行赋值?我想那一定已经有几十年了。另一方面,我将其设置为斜体,使其在某种程度上;-)
0赞 Matt 9/19/2011
有趣的是,我遇到过使用 only 而不是 .这最初是一个意外(来自其他语言的 habbit),但我注意到 PostgreSQL 愿意创建这些函数并且它们运行良好,所以我坚持使用它。=:=
2赞 Mike Sokolov 9/23/2011
@A.H.你应该看看 xquery - 它是在过去 5-10 年的某个时候发明的,正在积极开发中,并使用 “:=”

答:

1赞 Mike Sokolov 9/23/2011 #1

阅读 Postgresql 9 文档:

此页面在有关运算符优先级的表中列出“=”作为赋值运算符。

但奇怪的是,这个页面(分配运算符文档)没有提到它。

评论

0赞 A.H. 9/23/2011
这正是重点:第一个链接是通用 SQL 部分,是此上下文中的比较运算符,如示例中所示。第二个链接是关于 PL/pgSQL 的,其中的赋值被描述为 。这个问题仍然悬而未决。:-)=:=
0赞 A.H. 9/23/2011
我必须中途纠正自己:第一个链接在优先级表中提到了“相等,赋值”,但在 SQL 上下文中赋值意味着 .UPDATE table SET column **=** value
0赞 Mike Sokolov 9/23/2011
是的,我明白你的意思 - 该文档是关于 SQL 的,而不是过程 SQL 包装语言。
3赞 A.H. 9/23/2011 #2

对我自己的问题的部分回答:

PL/pgSQL 部分获取结果状态显示了两个使用特殊语法的示例:

GET DIAGNOSTICS variable = item [ , ... ]; 
GET DIAGNOSTICS integer_var = ROW_COUNT;

我两者都试过了,它们都有效。:==

但是是特殊的语法,所以可以说,这也不是正常的PL / pgSQL赋值操作。GET DIAGNOSTICS

评论

1赞 Pavel Stehule 5/14/2014
GET DIAGNOSTICS 由 ANSI/SQL 定义,SQL 使用“=”进行比较和分配。
58赞 filiprem 10/4/2011 #3

在 PL/PgSQL 解析器中,赋值运算符定义为

assign_operator : '='
                | COLON_EQUALS
                ;

这是一个遗留功能,自 1998 年引入以来就存在于源代码中 - 正如我们在 PostgreSQL Git 存储库中看到的那样。

从版本 9.4 开始,它被正式记录在案。

这种特质 - 有两个运算符用于同一事物 - 在pgsql用户列表中被提出,有些人要求将其删除,但它仍然保留在核心中,因为遗留代码的公平语料库依赖于它。

请参阅来自 Tom Lane(核心 Pg 开发人员)的此消息

因此,要直接回答您的问题:

我没有在文档中找到一些提到和/或解释的部分吗? 这?

您没有找到它,因为它没有文档,从版本 9.4 开始已修复。

使用 = 而不是 := 是否有任何已知的后果。

使用 = 没有附带后果,但您应该使用 := 进行赋值,以使代码更具可读性,并且(作为副作用)与 PL/SQL 更兼容。

更新:在极少数情况下可能会有副作用(参见 Erwin 的回答)


更新:由于丹尼尔、桑迪和其他人的输入,答案已更新。

评论

2赞 A.H. 10/4/2011
感谢您的深入研究。我发现非常有趣的是,PostgreSQL邮件列表上的问题在我的问题发布前几周就出现了。看来,是时候了;-)
0赞 Pacerier 4/6/2015
@filiprem,关于“面向未来”,嗯......将来不太可能打破。=
1赞 Sandy 6/5/2015
在你的回答中,你写了关于冒号等于的这段话:“它计划被移除,但仍然保留在里面,因为有些人依赖它”。这应该意味着,如果他们继续执行计划,使用冒号相等可能会导致问题。但是,在回答的最后,你写了“使用 = 没有附带后果,但你应该使用 := 进行赋值,以使你的代码面向未来。这两种说法不是相互矛盾吗?
1赞 filiprem 6/19/2015
@Sandy,对。我宁愿说“在被标记为过时多年后,它可以被删除。关于“副作用”:可能有一些。不频繁,但有可能。无论如何,删除这个小陷阱不值得破坏遗留代码。我敢打赌,我们至少会拥有几年。
2赞 Daniel Vérité 6/22/2015
版本 9.4 开始,它记录为赋值语法,因此占主导地位的选项是完全支持赋值。variable { := | = } expression=
41赞 Erwin Brandstetter 2/25/2014 #4

问题1

这终于被添加到了 Postgres 9.4 的官方文档中

对 PL/pgSQL 变量的值赋值写成:

variable { := | = } expression;

[...]可以使用 equal () 代替 PL/SQL 兼容。=:=

问题2

是否有任何已知的后果使用代替?=:=

是的,我有一个后果严重的案例:使用命名参数的函数调用 - 这是相关的,但不完全相同。

严格来说,在这种情况下,区别是在 SQL 代码中进行的。但对于毫无戒心的程序员来说,这是一种学术上的差异。1

考虑以下函数:

CREATE FUNCTION f_oracle(is_true boolean = TRUE)  -- correct use of "="
  RETURNS text
  LANGUAGE sql AS
$func$
SELECT CASE $1
       WHEN TRUE  THEN 'That''s true.'
       WHEN FALSE THEN 'That''s false.'
       ELSE 'How should I know?'
       END
$func$;

请注意函数定义中的正确用法。这是语法的一部分 - 采用 SQL 赋值的样式。阿拉伯数字=CREATE FUNCTION

使用命名表示法的函数调用:

SELECT * FROM f_oracle(is_true := TRUE);

Postgres 标识为参数分配,一切都很好。但是:=

SELECT * FROM f_oracle(is_true = TRUE);

由于 = 是 SQL 相等运算符,因此 Postgres 在调用语句的上下文中解释为 SQL 表达式,并尝试在将结果作为未命名的位置参数传递之前对其进行评估。它在外部作用域中查找标识符。如果找不到:is_true = TRUEis_true

ERROR:  column "is_true" does not exist

这是幸运的情况,幸运的是,也是常见的情况。

When can be found in the outer scope(并且数据类型兼容)是一个有效的表达式,其结果被函数接受。不会发生错误。显然,这是程序员使用 SQL 相等运算符的意图......is_trueis_true = TRUEboolean=

此 db<>fiddle 演示了该效果。
sqlfiddle

如果您不知道 和 之间的区别,则很难调试。
始终使用正确的运算符。
=:=


1 在函数调用中使用命名表示法时,只有 := 是正确的赋值运算符。这适用于所有语言的函数,而不仅仅是 PL/pgSQL,直到第 9.4 页。见下文。

2 可以使用 =(或 )来定义函数参数的默认值。这与手头的问题没有任何关系。它只是非常接近不正确的用例。DEFAULT

Postgres 9.0 - 9.4:从:==>

分配给命名函数参数的 SQL 标准是 =>Oracle 的 PL/SQL 使用它。Postgres 无法执行相同的操作,因为运算符以前是未保留的,因此它改用 PL/pgSQL 的赋值运算符。随着 Postgres 9.0 的发布,已弃用用于其他目的。发行说明:==>

不推荐使用 => 作为运算符名称 (Robert Haas)

PostgreSQL 的未来版本可能会拒绝此运算符名称 完全,以便支持 SQL 标准表示法 函数参数。目前,它仍然是允许的,但 定义此类运算符时,将发出警告。

如果您应该用于其他用途,请停止并停止。它将来会坏掉。=>

Postgres 9.5:立即使用=>

从此版本开始,将使用 SQL 标准运算符 =>。 仍支持向后兼容。但是在不需要在非常旧的版本上运行的新代码中使用标准运算符。:=

这适用于函数调用(SQL 作用域)中的命名参数赋值,不适用于 plpgsql 代码中的赋值运算符,后者保持不变。:=