问题 PostgreSQL“IF”语法错误


我是PostgreSQL的新手,我已经遇到了第一个问题..

我按照手动分步编写了一些代码来了解事务的工作原理。

为了简短起见,我创建了2个表,用户和移动:第一个是名称,电子邮件和信用列,第二个是from,to,import。

所以,我是这样尝试的:

BEGIN;
INSERT INTO movements (from, to, import) VALUES ('mary', 'steve', 600);
UPDATE users SET credit = credit - 600 WHERE name = 'mary';
UPDATE users SET credit = credit + 600 WHERE name = 'steve';
--here comes the problem!
IF (SELECT credit FROM users WHERE name = 'mary') < 0 THEN
 ROLLBACK;
END IF
COMMIT;

我总是得到错误:

错误:“IF”处或附近的语法错误

我哪里弄错了?

P.S。:不要专注于示例功能,这只是我理解交易的试验..现在,IF条款......


3016
2018-04-29 14:58


起源

我已根据您的要求添加了示例代码。这应该可以帮到你。 :-) - pyrocumulus
我认为其他人已经回答了语法问题,但我有一个评论。看起来您的代码试图维护的不变量可以通过信用列上的检查约束来维护。 - Warren
使用检查约束,然后您不需要此构造。 - Frank Heikens
@frank:我试图学习热门交易的作品,这只是一个例子;) - Strae


答案:


正如Johannes已经说过的那样:你正在将常规SQL与存储过程语言PL / pgSQL混合使用。 Johannes提供的链接应该向您解释存储过程的概念。

我认为你是以脚本的形式做的吗?一个接一个地执行一个声明?我担心您只能在存储过程或函数内执行您想要执行的操作,就像您可能会调用它一样。这是因为当您以这种方式执行语句时,每个语句都独立存在,没有关于其他语句的关系或信息。

此外,您可以查看以下链接,了解有关如何使用IF的更多信息...那么... ELSE ... END IF; plpgsql里面的条件: 链接


编辑:

我不知道此时是否允许ROLLBACK(因为每个存储过程已经在它自己的事务中),但你必须能够使用大量文档@自己解决这个问题。 http://www.postgresql.org。这是一个包含代码的示例函数,还演示了一些其他语法:

CREATE OR REPLACE FUNCTION public.test()
RETURNS integer AS
$$
DECLARE
tempvar integer;

BEGIN    
     tempvar := 1;

     INSERT INTO movements (from, to, import) VALUES ('mary', 'steve', 600);
     UPDATE users SET credit = credit - 600 WHERE name = 'mary';
     UPDATE users SET credit = credit + 600 WHERE name = 'steve';

     --here comes the problem!
     IF (SELECT credit FROM users WHERE name = 'mary') < 0 THEN
        ROLLBACK;
     END IF;

     RETURN tempvar;
END
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;

但是,如果你真的走这条路,我建议使用GUI数据库管理器。学习这一切更容易。


6
2018-04-29 15:16



好的,我已经理解并正在阅读johannes链接(我没有看到第一次看到答案)。你能发一个例子,看看我在PL / pgSQL中写的sql怎么样?谢谢.. - Strae
谢谢你的伴侣,我发现postgres文档非常好并且写得很好......只是很少'大',但没关系,我已经购买了3卷纸版本,我保证会全部阅读。我只是无法理解为什么if语句不能在普通的sql中使用,在mysql中,它也可以使用它... - Strae
何,最后一件事:我已经在谷歌上搜索,甚至在手册的操作部分,但我找不到':='的含义..用于什么? - Strae
运算符':='用于为变量赋值,而不需要相应的查询。 - pyrocumulus


你似乎使用普通的 SQL 但是 IF 声明是其中的一部分 PL/pgSQL 程序语言是PostgreSQL的一部分。


2
2018-04-29 15:02



可以解释得更好吗? - Strae


您可以尝试修改IF部分,来自:

IF (SELECT credit FROM users WHERE name = 'mary') < 0 THEN
 ROLLBACK;
END IF

SELECT SUM(credit) INTO v_credit FROM users WHERE name = 'mary';
IF (v_credit) < 0 THEN
 ROLLBACK;
END IF

假设v_credit是您之前定义的变量。 恕我直言,Postgre假设SELECT查询返回多个结果,即使您非常确定它是唯一的。所以我认为您可以先尝试将值分配给变量。


1
2018-04-30 04:50



为了测试它,我甚至尝试了IF 2 = 2那么[...]但仍然抛出错误.. - Strae


如果你想避免如果你可以重写你的代码:

BEGIN;

    INSERT INTO movements (from, to, import)    
    SELECT 'mary', 'steve', CASE credit < 600 WHEN TRUE THEN 0 ELSE 600 END;

    UPDATE users SET credit = credit - CASE credit < 600 WHEN TRUE THEN 0 ELSE 600 END    
    WHERE name = 'mary';

    UPDATE users u SET u.credit = u.credit + CASE v.credit < 600 WHEN TRUE THEN 0 ELSE 600 END    
    FROM users v    
    WHERE u.name = 'steve' and v.name = 'mary'

COMMIT;

是的,这是愚蠢的:)。


1
2018-04-29 15:17



大声笑我postgresql新..我认为已经了解你的例子;) - Strae
这是一个可怕的例子,真的,这只是一个思想实验:)。 - Adam Luter


与Microsoft的SQL和T / SQL类似,如果序列正确,您应该能够将常规SQL与PL / pgSQL混合使用。这是一个示例,其中序列在混合SQL / PL存储过程中很重要:

您无法在条件语句中包装条件语句 - 您必须将光标放在条件语句中。如果你以相反的方式执行序列,则会得到与您所看到的相同的错误,'错误:语法错误处于或接近“IF”':

CREATE OR REPLACE FUNCTION getSubsystemFaultListCount(_bunoid integer, _subsystem text, _starttime timestamp without time zone, _stoptime timestamp without time zone)
      RETURNS refcursor AS
    $BODY$
    DECLARE mycurs refcursor;
    BEGIN 
        IF _subsystem = 'ALL' THEN
            OPEN mycurs FOR
            SELECT  count(*), fs_fault.faultcode, fs_fault.downloadtime
            FROM    fs_fault
            WHERE   fs_fault.bunoid = _bunoid
                AND fs_fault.statusid IN(2, 4)
                AND fs_fault.downloadtime BETWEEN _starttime AND _stoptime
            GROUP BY fs_fault.faultcode, fs_fault.downloadtime;
            RETURN mycurs;
        ELSE
            OPEN mycurs FOR
            SELECT  count(*), fs_fault.faultcode, fs_fault.downloadtime
            FROM    fs_fault
            WHERE   fs_fault.bunoid = _bunoid
                AND fs_fault.subsystemid 
                    IN(SELECT id FROM fs_subsystem WHERE type = _subsystem)
                AND fs_fault.statusid IN(2, 4)
                AND fs_fault.downloadtime BETWEEN _starttime AND _stoptime
            GROUP BY fs_fault.faultcode, fs_fault.downloadtime;
            RETURN mycurs;
        END IF;

    END;
    $BODY$

我是PostGresSQL的初学者;这个功能只是一个例子。


0