PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Postgresql:TRANSAKTION VORBEREITEN

Ja, es ist möglich, aber brauchen Sie es wirklich?

Denken Sie zweimal nach, bevor Sie entscheiden, dass dies wirklich zwei getrennte Datenbanken sein müssen.

Sie könnten einfach beide Verbindungen offen halten und den ersten Befehl ROLLBACKEN, wenn der zweite fehlschlägt.

Wenn Sie wirklich vorbereitete Transaktionen benötigen, lesen Sie weiter.

In Bezug auf Ihr Schema - ich würde Sequenzgeneratoren und RETURNING-Klauseln auf Datenbankseite verwenden, nur der Einfachheit halber.

CREATE TABLE tbl_album (
  id    serial PRIMARY KEY,
  name  varchar(128) UNIQUE,
  ...
);
CREATE TABLE tbl_user_album (
  id          serial PRIMARY KEY,
  album_id    bigint NOT NULL,
  ...
);

Jetzt brauchen Sie einen externen Kleber - Distributed Transaction Coordinator (?) - damit das richtig funktioniert.

Der Trick besteht darin, PREPARE TRANSACTION statt COMMIT . Nachdem beide Transaktionen erfolgreich waren, verwenden Sie COMMIT PREPARED .

Der PHP-Proof-of-Concept ist unten.

ACHTUNG! diesem Code fehlt das kritische Teil - das ist Fehlerkontrolle. Jeder Fehler in $db2 sollte abgefangen und ROLLBACK VORBEREITET werden sollte auf $db1 ausgeführt werden Wenn Sie keine Fehler finden, verlassen Sie $db1 mit eingefrorenen Transaktionen, was wirklich, wirklich schlecht ist.

<?php
$db1 = pg_connect( "dbname=db1" );
$db2 = pg_connect( "dbname=db2" );
$transid = uniqid();

pg_query( $db1, 'BEGIN' );
$result = pg_query( $db1, "INSERT INTO tbl_album(name) VALUES('Absolutely Free') RETURNING id" );
$row = pg_fetch_row($result);
$albumid = $row[0];
pg_query( $db1, "PREPARE TRANSACTION '$transid'" );
if ( pg_query( $db2, "INSERT INTO tbl_user_album(album_id) VALUES($albumid)" ) ) {
    pg_query( $db1, "COMMIT PREPARED '$transid'" );
}
else {
    pg_query( $db1, "ROLLBACK PREPARED '$transid'" );
}
?>

Und noch einmal - denken Sie nach, bevor Sie es verwenden. Was Erwin vorschlägt, könnte sinnvoller sein.

Oh, und noch eine Anmerkung ... Um diese PostgreSQL-Funktion zu verwenden, müssen Sie max_prepared_transactions config-Variable auf einen Wert ungleich Null.