12

我是 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”处或附近的语法错误

我错在哪里?

PS:不要专注于示例功能,这只是我理解交易的尝试......现在,IF子句......

4

5 回答 5

7

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

我认为你这样做是作为一个脚本?执行一个又一个语句?恐怕您只能在存储过程或函数中做您想做的事情,正如您所说的那样。这是因为当您以这种方式执行语句时,每个语句都是独立的,与其他语句没有任何关系或信息。

此外,您可以查看以下链接以获取有关如何使用 IF ... THEN ... 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 DB 管理器。学习这一切更容易。

于 2009-04-29T15:16:19.380 回答
2

您似乎使用plain SQL,但该IF语句是PL/pgSQLPostgreSQL一部分的过程语言的一部分。

于 2009-04-29T15:02:50.223 回答
1

如果您想避免这种情况,您可以将代码重写为:

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;

是的,这很愚蠢:)。

于 2009-04-29T15:17:23.793 回答
1

您可以尝试修改 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 查询返回多个结果,即使您非常确定它是唯一的。因此,我认为您可以尝试先将值分配给变量。

于 2009-04-30T04:50:32.530 回答
0

与 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 的初学者;这个函数只是一个例子。

于 2012-04-06T16:46:22.377 回答