2009-04-29 36 views
11

Sono nuovo con PostgreSQL, e ho già il mio primo problema ..PostgreSQL "IF" errore di sintassi

ho scritto il codice per capire come funzionano le transazioni, seguendo passo dopo passo manuale.

Per farla breve, ho creato 2 tabelle, utenti e movimenti: nella prima ci sono le colonne nome, email e credito, nella seconda le colonne da, a, importazione.

Così, ho cercato in questo modo:

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; 

ottengo sempre l'errore:

ERROR: syntax error at or near "IF"

Dove mi sbaglio?

PS: non si concentrano sulla funzionalità ad esempio, è solo una prova per me capire le transazioni .. e ora, la clausola IF ...

+0

Ho aggiunto codice di esempio, dalla vostra richiesta. Questo dovrebbe aiutarti. :-) – pyrocumulus

+0

Utilizza un vincolo di controllo, quindi non hai bisogno di questa costruzione. –

+0

@frank: stavo cercando di imparare le operazioni con le transazioni a caldo, questo è solo un esempio;) – Strae

risposta

6

Come già detto Johannes: si sta mescolando SQL regolare con PL/pgSQL, la lingua della procedura memorizzata. Il collegamento fornito da Johannes dovrebbe spiegare il concetto di procedure memorizzate.

Immagino che lo stai facendo come una sceneggiatura? Esecuzione di una dichiarazione dopo l'altra? Temo che tu possa solo fare ciò che vuoi fare all'interno di una stored procedure o di una funzione, come potresti chiamarla. Questo perché quando esegui le affermazioni in questo modo, ogni affermazione è indipendente, senza alcuna relazione o informazione rispetto alle altre dichiarazioni.

Inoltre è possibile consultare il seguente collegamento per ulteriori informazioni su come utilizzare IF ... THEN ... ELSE ... END IF; condizionali all'interno di plpgsql: link.


EDIT:

Non so se ROLLBACK è consentito a quel punto (perché ogni stored procedure è già nella propria transazione), ma si deve essere in grado di capirlo per te stesso utilizzando la documentazione estesa @http://www.postgresql.org. Ecco una funzione di esempio con il codice in esso, anche dimostrando qualche altra sintassi:

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; 

Tuttavia, se siete davvero questa strada, mi consiglia di utilizzare un gestore di GUI DB. È più facile imparare tutto questo.

+0

Ok, ho capito e sto leggendo il link johannes (che non ho visto la prima volta ho letto la risposta). Puoi pubblicare un esempio di come può sembrare lo sql che ho scritto in PL/pgSQL? Grazie .. – Strae

+0

Grazie amico, ho trovato la documentazione postgres molto buona e ben scritta .. solo un po '"grande", ma va bene, ho acquistato la versione in carta di 3 volumi e prometto di leggerli tutti. Non riesco proprio a capire perché l'istruzione if non può essere usata in plain sql quando, in mysql, può anche essere usata pure. – Strae

+0

Ho, ultima cosa: ho cercato su Google e anche nella sezione operatore del manuale, ma Non riesco a trovare il significato di ': =' .. a cosa serve? – Strae

2

Sembrate usare pianura SQL ma la dichiarazione IF fa parte del linguaggio procedurale PL/pgSQL che fa parte di PostgreSQL.

+1

può spiegare meglio questo? – Strae

1

Se si vuole evitare il se si potrebbe riscrivere il codice come:

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ì, questo è stupido :).

+0

lol im new di postgresql .. penso di aver capito il tuo esempio;) – Strae

+0

È un esempio terribile, in realtà, era solo un esperimento mentale :). –

1

Si potrebbe provare a modificare la parte IF, da:

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 

Supponendo v_credit è una variabile definita in precedenza. IMHO, Postgre presuppone che la query SELECT restituisca più di un risultato, anche se sei assolutamente certo che sia univoco. Quindi penso che potresti provare prima ad assegnare il valore a una variabile.

+0

per testarlo, ho persino provato l'IF 2 = 2 THEN [...] ma ancora lancio l'errore .. – Strae

0

Simile a SQL di Microsoft e T/SQL, si dovrebbe essere in grado di mescolare SQL regolare con PL/pgSQL se sono nella sequenza corretta. Ecco un esempio in cui la sequenza è importante in un processo memorizzato SQL/PL misto:

Non è possibile includere istruzioni condizionali all'interno del cursore: è necessario posizionare il cursore all'interno dell'istruzione condizionale. Se fate la sequenza viceversa, si otterrà lo stesso errore di aver visto, 'ERRORE: errore di sintassi nei pressi o '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$ 

Io sono un principiante in PostgreSQL; questa funzione è solo un esempio.

Problemi correlati