PL/pgSQL is PostgreSQL’s default procedural language. It extends SQL with control structures, variables, cursors, and exception handling.
CREATEEXTENSIONplpgsql;-- installed by default
-- Basic function with variables and control flow
CREATEFUNCTIONcalculate_discount(pricenumeric,quantityinteger)RETURNSnumericLANGUAGEplpgsqlAS$$DECLAREdiscountnumeric:=0;BEGINIFquantity>=100THENdiscount:=0.20;ELSIFquantity>=50THENdiscount:=0.10;ELSIFquantity>=10THENdiscount:=0.05;ENDIF;RETURNprice*quantity*(1-discount);END;$$;-- Loop and set-returning function
CREATEFUNCTIONfibonacci(ninteger)RETURNSSETOFintegerLANGUAGEplpgsqlAS$$DECLAREainteger:=0;binteger:=1;tmpinteger;BEGINFORiIN1..nLOOPRETURNNEXTa;tmp:=a+b;a:=b;b:=tmp;ENDLOOP;END;$$;SELECT*FROMfibonacci(10);-- Exception handling
CREATEFUNCTIONsafe_divide(anumeric,bnumeric)RETURNSnumericLANGUAGEplpgsqlAS$$BEGINRETURNa/b;EXCEPTIONWHENdivision_by_zeroTHENRAISENOTICE'Division by zero, returning NULL';RETURNNULL;END;$$;-- Trigger function
CREATEFUNCTIONupdate_modified_column()RETURNStriggerLANGUAGEplpgsqlAS$$BEGINNEW.modified_at=now();RETURNNEW;END;$$;CREATETRIGGERset_modifiedBEFOREUPDATEONmy_tableFOREACHROWEXECUTEFUNCTIONupdate_modified_column();-- Procedure with transaction control (PG 11+)
CREATEPROCEDUREbatch_archive(batch_sizeinteger)LANGUAGEplpgsqlAS$$DECLARErows_movedinteger;BEGINLOOPWITHmovedAS(DELETEFROMordersWHEREstatus='completed'RETURNING*)INSERTINTOorders_archiveSELECT*FROMmoved;GETDIAGNOSTICSrows_moved=ROW_COUNT;COMMIT;EXITWHENrows_moved<batch_size;ENDLOOP;END;$$;