Ich verwende SQL Server 2008 Standard, der keine SEQUENCE
Funktion hat.
Ein externes System liest Daten aus mehreren dedizierten Tabellen der Hauptdatenbank. Das externe System speichert eine Kopie der Daten und überprüft diese regelmäßig auf Änderungen der Daten und aktualisiert ihre Kopie.
Um die Synchronisierung effizient zu gestalten, möchte ich nur Zeilen übertragen, die seit der vorherigen Synchronisierung aktualisiert oder eingefügt wurden. (Die Zeilen werden nie gelöscht). Um zu wissen, welche Zeilen seit der letzten Synchronisierung aktualisiert oder eingefügt wurden, befindet sich in jeder Tabelle eine bigint
Spalte RowUpdateCounter
.
Die Idee ist, dass sich die Nummer in ihrer RowUpdateCounter
Spalte jedes Mal ändert , wenn eine Zeile eingefügt oder aktualisiert wird . Die Werte, die in die RowUpdateCounter
Spalte eingegeben werden, sollten einer ständig wachsenden Folge von Zahlen entnommen werden. Die Werte in der RowUpdateCounter
Spalte sollten eindeutig sein und jeder neue in einer Tabelle gespeicherte Wert sollte größer sein als jeder vorherige Wert.
Bitte beachten Sie die Skripte, die das gewünschte Verhalten zeigen.
Schema
CREATE TABLE [dbo].[Test](
[ID] [int] NOT NULL,
[Value] [varchar](50) NOT NULL,
[RowUpdateCounter] [bigint] NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED
(
[ID] ASC
))
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_RowUpdateCounter] ON [dbo].[Test]
(
[RowUpdateCounter] ASC
)
GO
Fügen Sie einige Zeilen ein
INSERT INTO [dbo].[Test]
([ID]
,[Value]
,[RowUpdateCounter])
VALUES
(1, 'A', ???),
(2, 'B', ???),
(3, 'C', ???),
(4, 'D', ???);
Erwartetes Ergebnis
+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | C | 3 |
| 4 | D | 4 |
+----+-------+------------------+
Die generierten Werte in RowUpdateCounter
können beispielsweise unterschiedlich sein 5, 3, 7, 9
. Sie sollten eindeutig sein und größer als 0 sein, da wir von einer leeren Tabelle ausgegangen sind.
Einige Zeilen einfügen und aktualisieren
DECLARE @NewValues TABLE (ID int NOT NULL, Value varchar(50));
INSERT INTO @NewValues (ID, Value) VALUES
(3, 'E'),
(4, 'F'),
(5, 'G'),
(6, 'H');
MERGE INTO dbo.Test WITH (HOLDLOCK) AS Dst
USING
(
SELECT ID, Value
FROM @NewValues
)
AS Src ON Dst.ID = Src.ID
WHEN MATCHED THEN
UPDATE SET
Dst.Value = Src.Value
,Dst.RowUpdateCounter = ???
WHEN NOT MATCHED BY TARGET THEN
INSERT
(ID
,Value
,RowUpdateCounter)
VALUES
(Src.ID
,Src.Value
,???)
;
Erwartetes Ergebnis
+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | E | 5 |
| 4 | F | 6 |
| 5 | G | 7 |
| 6 | H | 8 |
+----+-------+------------------+
RowUpdateCounter
für Zeilen mit ID1,2
sollte unverändert bleiben, da diese Zeilen nicht geändert wurden.RowUpdateCounter
für Zeilen mit ID3,4
sollte sich ändern, da sie aktualisiert wurden.RowUpdateCounter
für Zeilen mit ID5,6
sollte sich ändern, da sie eingefügt wurden.RowUpdateCounter
für alle geänderten Zeilen sollte größer als 4 sein (die letzteRowUpdateCounter
aus der Sequenz).
Die Reihenfolge, in der neuen Werten ( 5,6,7,8
) geänderten Zeilen zugewiesen werden, spielt keine Rolle. Die neuen Werte können z. B. Lücken aufweisen, 15,26,47,58
sollten jedoch niemals abnehmen.
Es gibt mehrere Tabellen mit solchen Zählern in der Datenbank. Es spielt keine Rolle, ob alle die einzelne globale Sequenz für ihre Nummern verwenden oder ob jede Tabelle ihre eigene individuelle Sequenz hat.
Ich möchte keine Spalte mit einem Datums- / Uhrzeitstempel anstelle eines Ganzzahlzählers verwenden, weil:
Die Uhr auf dem Server kann sowohl vorwärts als auch rückwärts springen. Besonders wenn es sich um eine virtuelle Maschine handelt.
Die von Systemfunktionen zurückgegebenen Werte
SYSDATETIME
sind für alle betroffenen Zeilen gleich. Der Synchronisierungsprozess sollte in der Lage sein, Änderungen in Stapeln zu lesen. Wenn die Stapelgröße beispielsweise 3 ZeilenMERGE
beträgt, liest der Synchronisierungsprozess nach dem obigen Schritt nur ZeilenE,F,G
. Wenn der Synchronisierungsprozess das nächste Mal ausgeführt wird, wird er ab Zeile fortgesetztH
.
Die Art, wie ich es jetzt mache, ist ziemlich hässlich.
Da es SEQUENCE
in SQL Server 2008 keine gibt , emuliere ich die SEQUENCE
durch eine dedizierte Tabelle mit IDENTITY
wie in dieser Antwort gezeigt . Dies an sich ist ziemlich hässlich und wird durch die Tatsache verschärft, dass ich nicht eine einzige, sondern eine Reihe von Zahlen gleichzeitig generieren muss.
Dann habe ich INSTEAD OF UPDATE, INSERT
auf jeder Tabelle einen Trigger mit dem RowUpdateCounter
und generiere dort die erforderlichen Zahlenmengen.
In den INSERT
, UPDATE
und MERGE
Abfragen setze ich RowUpdateCounter
auf 0, die durch die richtigen Werte im Trigger ersetzt wird. Die ???
in den obigen Abfragen sind 0
.
Es funktioniert, aber gibt es eine einfachere Lösung?
quelle
rowversion
würde mir diese Möglichkeit nicht geben, wenn ich richtig verstehe, was es ist ... Wird es garantiert immer größer?rowversion
. Es sieht sehr verlockend aus. Meine einzige Sorge ist, dass sich alle Beispiele für die Verwendung, die ich bisher gesehen habe, darauf konzentrieren, festzustellen, ob sich eine einzelne Zeile geändert hat. Ich brauche eine effiziente Art und Weise zu wissen , was setzte seit einem bestimmten Zeitpunkt geändert von Zeilen. Ist es außerdem möglich, ein Update zu verpassen?A
aktualisiert eine Zeile, ihre Zeilenversion ändert sich auf 123,A
ist noch nicht festgeschrieben. time = 2: Die TransaktionB
aktualisiert eine weitere Zeile, ihre Zeilenversion ändert sich in 124. time = 3:B
Commits. Zeit = 4: Der Synchronisierungsprozess wird ausgeführt und ruft alle Zeilen mit einer Zeilenversion> 122 ab. Dies bedeutet, dass die Zeilen nur von aktualisiert werdenB
. Zeit = 5:A
Commits. Ergebnis: Änderungen vonA
werden vom Synchronisierungsprozess niemals erfasst. Liege ich falsch? VielleichtMIN_ACTIVE_ROWVERSION
hilft ein kluger Einsatz ?Antworten:
Sie können hierfür eine
ROWVERSION
Spalte verwenden.Die Dokumentation besagt, dass
Die Werte sind
BINARY(8)
und Sie sollten sie als betrachten ,BINARY
anstattBIGINT
wie nach0x7FFFFFFFFFFFFFFF
es auf geht0x80...
und beginnt Aufarbeitung von ,-9223372036854775808
wenn sie als unterzeichnet behandeltbigint
.Ein vollständig ausgearbeitetes Beispiel finden Sie unten. Das Verwalten des Index für die
ROWVERSION
Spalte ist teuer, wenn Sie viele Updates haben. Daher möchten Sie möglicherweise Ihre Arbeitslast mit und ohne testen, um festzustellen, ob sich die Kosten lohnen.quelle
@@DBTS
geben sollteMIN_ACTIVE_ROWVERSION()
, und wennMIN_ACTIVE_ROWVERSION()
Vergleich<=
verwendet werden sollte<
und>
werden>=
.@@DBTS
undMIN_ACTIVE_ROWVERSION()
wenn es aktive nicht festgeschriebene Transaktionen gibt. Wenn eine Anwendung@@DBTS
eher als verwendetMIN_ACTIVE_ROWVERSION
, können Änderungen übersehen werden, die bei der Synchronisierung aktiv sind.Haben Sie versucht, die
IDENTITY
Option zu verwenden?Zum Beispiel:
wo
Dies ähnelt SEQUENCE in Oracle.
quelle
IDENTITY
tut nicht das, was für das automatische Inkrementieren von Updates und Einfügungen erforderlich ist .IDENTITY
helfen kann.