Синтаксическая ошибка 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...
Ответы
Ответ 1
Как уже говорит Йоханнес: вы смешиваете обычный SQL с PL/pgSQL, языком хранимой процедуры. Ссылка, которую предлагает Johannes, должна объяснить вам концепцию хранимых процедур.
Я так понимаю, вы делаете это как script? Выполнение одного заявления за другим? Боюсь, вы можете делать то, что хотите делать внутри хранимой процедуры или функции, как вы могли бы ее назвать. Это происходит потому, что, когда вы выполняете инструкции таким образом, каждый оператор стоит сам по себе без каких-либо отношений или информации относительно других операторов.
Кроме того, вы можете посмотреть следующую ссылку для получения дополнительной информации о том, как использовать IF... THEN... ELSE... END IF; условные выражения внутри plpgsql: ссылка.
EDIT:
Я не знаю, разрешен ли 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;
Однако, если вы действительно идете по этому пути, я рекомендую использовать диспетчер DBI. Это легче изучить все это.
Ответ 2
Похоже, вы используете plain SQL
, но оператор IF
является частью процедурного языка PL/pgSQL
, который является частью PostgreSQL.
Ответ 3
Если вы хотите избежать, если вы можете переписать свой код как:
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;
Да, это глупо:).
Ответ 4
Вы можете попробовать изменить часть 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 - это переменная, которую вы определили ранее.
IMHO, Postgre предполагает, что запрос SELECT возвращает более одного результата, даже если вы уверены, что он уникален. Поэтому я думаю, вы могли бы попытаться заранее присвоить значение переменной.
Ответ 5
Подобно Microsoft SQL и T/SQL, вы должны иметь возможность смешивать обычный SQL с PL/pgSQL, если они находятся в правильной последовательности. Здесь пример, где последовательность имеет значение в смешанном SQL/PL сохраненном proc:
Вы не можете обертывать условные выражения внутри курсора - вы должны поместить курсор внутрь условного оператора. Если вы выполните последовательность наоборот, вы получите ту же ошибку, что и вы, "ERROR: синтаксическая ошибка в или рядом с" 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; эта функция является всего лишь примером.