Ich verwende SQL Server 2008 Standard, der keine SEQUENCEFunktion 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 bigintSpalte RowUpdateCounter.
Die Idee ist, dass sich die Nummer in ihrer RowUpdateCounterSpalte jedes Mal ändert , wenn eine Zeile eingefügt oder aktualisiert wird . Die Werte, die in die RowUpdateCounterSpalte eingegeben werden, sollten einer ständig wachsenden Folge von Zahlen entnommen werden. Die Werte in der RowUpdateCounterSpalte 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 RowUpdateCounterkö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 |
+----+-------+------------------+
RowUpdateCounterfür Zeilen mit ID1,2sollte unverändert bleiben, da diese Zeilen nicht geändert wurden.RowUpdateCounterfür Zeilen mit ID3,4sollte sich ändern, da sie aktualisiert wurden.RowUpdateCounterfür Zeilen mit ID5,6sollte sich ändern, da sie eingefügt wurden.RowUpdateCounterfür alle geänderten Zeilen sollte größer als 4 sein (die letzteRowUpdateCounteraus 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,58sollten 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
SYSDATETIMEsind für alle betroffenen Zeilen gleich. Der Synchronisierungsprozess sollte in der Lage sein, Änderungen in Stapeln zu lesen. Wenn die Stapelgröße beispielsweise 3 ZeilenMERGEbeträ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 SEQUENCEin SQL Server 2008 keine gibt , emuliere ich die SEQUENCEdurch eine dedizierte Tabelle mit IDENTITYwie 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, INSERTauf jeder Tabelle einen Trigger mit dem RowUpdateCounterund generiere dort die erforderlichen Zahlenmengen.
In den INSERT, UPDATEund MERGEAbfragen setze ich RowUpdateCounterauf 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?
rowversionwü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?
Aaktualisiert eine Zeile, ihre Zeilenversion ändert sich auf 123, Aist noch nicht festgeschrieben. time = 2: Die Transaktion Baktualisiert eine weitere Zeile, ihre Zeilenversion ändert sich in 124. time = 3: BCommits. Zeit = 4: Der Synchronisierungsprozess wird ausgeführt und ruft alle Zeilen mit einer Zeilenversion> 122 ab. Dies bedeutet, dass die Zeilen nur von aktualisiert werden B. Zeit = 5: ACommits. Ergebnis: Änderungen von Awerden vom Synchronisierungsprozess niemals erfasst. Liege ich falsch? Vielleicht MIN_ACTIVE_ROWVERSIONhilft ein kluger Einsatz ?