2016-05-05 13 views
0

Ich habe zwei Tabellen (dies ist ein sehr vereinfachtes Modell meines Anwendungsfall):SQL: update Spaltenzähler und legen neue Zeile

- TableCounter with 2 columns: idEntry, counter 
- TableObject with 1 column : idEntry , seq (with the pair idEntry/seq unique) 

Ich brauche in 1-Transaktion in der Lage:

- increase counter for idEntry = x 
- insert (x,new_counter_value) in the TableObject. 

wissen, dass ich keine Sequenz verlieren muss, und es ist eine Transaktion sehr gleichzeitig und viel aufgerufen.

Wie würden Sie eine solche Transaktion in eine Anweisung schreiben (nicht für eine gespeicherte Prozedur)? Würden Sie die Zeile von TableCounter für idEntry = x sperren?

Bisher habe ich das, aber ich suche nach einer besseren Lösung.

BEGIN TRANSACTION; 
SELECT counter FROM TableCounter WHERE idEntry=1 FOR UPDATE; 
UPDATE TableCounter SET counter=counter+1 WHERE idEntry=1; 
INSERT INTO TableObject(idEntry, seq) SELECT TableCounter.idEntry, TableCounter.counter FROM TableCounter WHERE TableCounter.idEntry = 1; 
COMMIT TRANSACTION 

Danke

+0

Sie dies in einem Trigger tun würde. –

+0

dies muss SQL db agnostic sein, also brauche ich eine SQL-Anweisung für diese – ptou

+0

Welche DBMS verwenden Sie? Das 'select for update' ist nutzlos. Führen Sie einfach das 'update' direkt aus, wodurch auch die Zeile gesperrt wird. –

Antwort

1

Die select for update ist nutzlos, wenn das nächste, was Sie sowieso die Zeile aktualisieren tun ist (dies ist für alle DBMS wahr, die select for update unterstützt)

Für Postgres dies getan werden kann in einer einzigen Anweisung eine Datenmodifizierungs CTE Verwendung:

with updated as (
    update tablecounter 
    set counter = counter + 1 
    where identry = 1 
    returning identry, counter 
) 
insert into tableobject (identry, seq) 
select identry, counter 
from updated; 

Das Update die Zeile verriegelt, wonach jede gleichzeitige einfügen/Aktualisierungsmittel (für die gleiche identry) muss warten, bis das obige Commit oder Rollback ist.


Wenn ich brauchte (wirklich) eine lückenlose Sequenz und ich konnte mit den Skalierbarkeitsprobleme einer solchen Lösung leben (da die Anforderung wichtiger ist dann die Leistung oder Skalierbarkeit) ich wahrscheinlich, dass in Funktion setzen würde. Etwa wie folgt:

definieren die Folge (= Zähler) Tabelle

create table gapless_sequence 
(
    entity text not null primary key, 
    sequence_value integer not null default 0 
); 

-- "create" a new sequence 
insert into gapless_sequence (entity) values ('some_table'); 
commit; 

nun eine Funktion erzeugen, die einen neuen Wert

create function next_value(p_entity text) 
    returns integer 
as 
$$ 
    update gapless_sequence 
    set sequence_value = sequence_value + 1 
    where entity = p_entity 
    returning sequence_value; 
$$ 
language sql; 

Gleiche wie oben behauptet: die Transaktion, die die nächste erwirbt Sequenz für eine Entität blockiert alle nachfolgenden Aufrufe der Funktion für dieselbe Entität, bis die erste Transaktion festgeschrieben (oder rückgängig gemacht) wird.

Jetzt definieren eine Tabelle, die die lückenlose Sequenz verwendet, ist ganz einfach:

create table some_table 
(
    id integer primary key default next_value('some_table'), 
    some_column text 
); 

Und dann tun Sie einfach:

insert into some_table (some_column) values ('foo'); 

Ein gleichzeitiger Einsatz in some_table würde warten, bis die erste Transaktion verpflichtet. Der update wird dann den festgeschriebenen Wert anzeigen und den entsprechenden nächsten Sequenzwert zurückgeben.

dieser Kurs kann auch ohne Verwendung eine default Klausel in der Tabellendefinition erfolgen, aber dann würden Sie brauchen, um die Funktion explizit in der Insert-Anweisung nennen:

insert into some_table 
    (id, some_column) 
values 
(next_value('some_table'), 'foo'); 

jedoch, dass das Potenzial pitfall hat, dass Nichts zwingt Sie dazu, beim Aufruf der Funktion den korrekten Entity-Namen zu verwenden.


Alle Beispiele oben gehen davon aus, dass Auto begehen gedreht wird off

+0

vorausgesetzt, Sie auch Skalierbarkeit, was wäre also die optimale Lösung, um eine lückenlose Sequenz zu erstellen, wenn Sie sicher wissen, dass die DBMS Postgresql ist und Sie können eines seiner Features nutzen? – ptou

+0

Sie können nicht lückenlos Sequenzen ** und ** skalierbare, gleichzeitige Einsätze haben. Sie müssen entscheiden, was wichtiger ist. Die Frage ist: Warum brauchen Sie eine lückenlose Sequenz? Der einzige Grund, den ich kenne, sind gesetzliche Anforderungen, z. Rechnungsnummern ** müssen in den meisten Ländern lückenlos sein. –