2009-04-29 32 views
11

Soy nuevo con PostgreSQL, y ya tengo mi primer problema ..PostgreSQL "SI" error de sintaxis

me escribió algo de código para entender cómo funcionan las transacciones, siguiendo el manual paso a paso.

Para abreviar, he creado 2 tablas, usuarios y movimientos: en la primera están las columnas de nombre, correo electrónico y crédito, en la segunda las columnas desde, hacia, importar.

Por lo tanto, yo estaba tratando de esta manera:

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; 

Siempre sale el error:

ERROR: syntax error at or near "IF"

¿Dónde estoy equivocado?

PD: no se centran en la funcionalidad ejemplo, es sólo una prueba para mí entender las transacciones .. y ahora, la cláusula IF ...

+0

He agregado el código de muestra, a petición suya. Esto debería ayudarte. :-) – pyrocumulus

+0

Use una restricción de verificación, entonces no necesita esta construcción. –

+0

@frank: estaba intentando aprender trabajos de transacciones calientes, es solo un ejemplo;) – Strae

Respuesta

6

Como Johannes ya dice: está mezclando SQL normal con PL/pgSQL, el lenguaje de procedimiento almacenado. El enlace que proporciona Johannes debe explicarle el concepto de procedimientos almacenados.

Supongo que lo estás haciendo como un script? Ejecutando una declaración después de otra? Me temo que solo puede hacer lo que quiera hacer dentro de un Procedimiento almacenado o Función, como podría llamarlo. Esto se debe a que cuando se ejecutan declaraciones de esta manera, cada declaración se sostiene por sí misma sin relación o información con respecto a las otras declaraciones.

Además, puede consultar el siguiente enlace para obtener más información sobre cómo usar IF ... THEN ... ELSE ... END IF; condicional dentro de plpgsql: link.


EDIT:

No sé si se permite que ROLLBACK en ese momento (ya que cada procedimiento almacenado ya está en su propia transacción), pero debe ser capaz de darse cuenta de eso para usted mismo usando la extensa documentación @http://www.postgresql.org. He aquí una función de ejemplo con su código en él, demostrando también alguna otra sintaxis:

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; 

Sin embargo, si realmente se va este camino, le recomiendo usar un gestor de interfaz gráfica de usuario DB. Es más fácil aprender todo esto.

+0

Ok, lo he entendido y estoy leyendo el enlace johannes (que no vi la primera vez que leí la respuesta). ¿Puedes publicar un ejemplo de cómo puede verse el sql que escribí en PL/pgSQL? Gracias. – Strae

+0

Gracias amigo, he encontrado la documentación de Postgres muy buena y bien escrita ... solo un poco 'grande', pero está bien, compré la versión en papel de 3 volúmenes y prometo leerlos todos. No entiendo por qué la instrucción if no se puede usar en sql simple cuando, en mysql, también se puede usar ... – Strae

+0

Ho, lo último: he buscado en Google e incluso en la sección de operador del manual, pero no puedo encontrar el significado de ': =' ... ¿para qué se usa? – Strae

2

Usted parece usar llanura SQL pero la declaración IF es parte del lenguaje de procedimiento PL/pgSQL que es parte de PostgreSQL.

+1

puede explicar mejor eso? – Strae

1

Si se quiere evitar el si pudiera volver a escribir el código como:

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; 

Sí, esto es estúpido :).

+0

lol im new of postgresql ... creo que entendí tu ejemplo;) – Strae

+0

Es un ejemplo terrible, realmente, fue solo un experimento mental :). –

1

Se podría tratar de modificar la parte SI, a partir de:

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

a

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

Suponiendo v_credit es una variable que ha definido anteriormente. En mi humilde opinión, Postgre asume que la consulta SELECT devuelve más de un resultado, aunque está seguro de que es único. Así que creo que podrías intentar asignar el valor a una variable primero de antemano.

+0

para probarlo, incluso he probado el IF 2 = 2 THEN [...] pero todavía arrojo el error ... – Strae

0

Al igual que SQL y T/SQL de Microsoft, debería poder mezclar SQL normal con PL/pgSQL si están en la secuencia correcta. Aquí hay un ejemplo donde la secuencia es importante en un proceso mixto SQL/PL almacenado:

No puede envolver declaraciones condicionales dentro del cursor; debe colocar el cursor dentro de la instrucción condicional. Si lo hace la secuencia a la inversa, obtendrá el mismo error que hubiera visto, 'ERROR: error de sintaxis en o cerca del 'SI'':

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$ 

Soy un principiante en PostgresSQL; esta función es solo un ejemplo.

Cuestiones relacionadas