Estoy usando SQL Server 2008 Standard, que no tiene una SEQUENCEfunción.
Un sistema externo lee datos de varias tablas dedicadas de la base de datos principal. El sistema externo mantiene una copia de los datos y verifica periódicamente los cambios en los datos y actualiza su copia.
Para que la sincronización sea eficiente, quiero transferir solo las filas que se actualizaron o insertaron desde la sincronización anterior. (Las filas nunca se eliminan). Para saber qué filas se actualizaron o insertaron desde la última sincronización, hay una bigintcolumna RowUpdateCounteren cada tabla.
La idea es que cada vez que se inserte o actualice una fila, el número en su RowUpdateCountercolumna cambiaría. Los valores que entran en la RowUpdateCountercolumna deben tomarse de una secuencia de números cada vez mayor. Los valores en la RowUpdateCountercolumna deben ser únicos y cada nuevo valor almacenado en una tabla debe ser mayor que cualquier valor anterior.
Consulte los scripts que muestran el comportamiento deseado.
Esquema
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
INSERTAR algunas filas
INSERT INTO [dbo].[Test]
([ID]
,[Value]
,[RowUpdateCounter])
VALUES
(1, 'A', ???),
(2, 'B', ???),
(3, 'C', ???),
(4, 'D', ???);
Resultado Esperado
+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | C | 3 |
| 4 | D | 4 |
+----+-------+------------------+
Los valores generados en RowUpdateCounterpueden ser diferentes, por ejemplo, 5, 3, 7, 9. Deben ser únicos y deben ser mayores que 0, ya que comenzamos desde una tabla vacía.
INSERTAR y ACTUALIZAR algunas filas
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
,???)
;
Resultado Esperado
+----+-------+------------------+
| ID | Value | RowUpdateCounter |
+----+-------+------------------+
| 1 | A | 1 |
| 2 | B | 2 |
| 3 | E | 5 |
| 4 | F | 6 |
| 5 | G | 7 |
| 6 | H | 8 |
+----+-------+------------------+
RowUpdateCounterpara las filas con ID1,2debe permanecer como está, porque estas filas no se modificaron.RowUpdateCounterpara las filas con ID3,4deberían cambiar, porque se actualizaron.RowUpdateCounterpara las filas con ID5,6deberían cambiar, porque se insertaron.RowUpdateCounterpara todas las filas modificadas debe ser mayor que 4 (el últimoRowUpdateCounterde la secuencia).
El orden en que 5,6,7,8se asignan los nuevos valores ( ) a las filas modificadas realmente no importa. Los nuevos valores pueden tener huecos, por ejemplo 15,26,47,58, pero nunca deberían disminuir.
Hay varias tablas con dichos contadores en la base de datos. No importa si todos usan la secuencia global única para sus números, o si cada tabla tiene su propia secuencia individual.
No quiero usar una columna con un sello de fecha y hora en lugar de un contador entero, porque:
El reloj en el servidor puede saltar hacia adelante y hacia atrás. Especialmente cuando está en una máquina virtual.
Los valores devueltos por las funciones del sistema
SYSDATETIMEson iguales para todas las filas afectadas. El proceso de sincronización debería poder leer los cambios en lotes. Por ejemplo, si el tamaño del lote es de 3 filas, luego delMERGEpaso anterior, el proceso de sincronización solo leería filasE,F,G. Cuando se ejecute el proceso de sincronización la próxima vez, continuará desde la filaH.
La forma en que lo hago ahora es bastante fea.
Como no existe SEQUENCEen SQL Server 2008, emulo SEQUENCEuna tabla dedicada con la IDENTITYque se muestra en esta respuesta . Esto en sí mismo es bastante feo y se ve exacerbado por el hecho de que necesito generar no uno solo, sino un lote de números a la vez.
Luego, tengo un INSTEAD OF UPDATE, INSERTactivador en cada tabla con RowUpdateCountery genero los conjuntos de números necesarios allí.
En las consultas INSERT, UPDATEy MERGEconfiguro RowUpdateCountera 0, que se reemplaza por los valores correctos en el disparador. El ???en las consultas anteriores son 0.
Funciona, pero ¿hay una solución más fácil?
rowversionno me daría esta posibilidad, si entiendo correctamente lo que es ... ¿Se garantiza que aumentará?
rowversion. Se ve muy tentador. Mi única preocupación es que todos los ejemplos de su uso que he visto hasta ahora giran en torno a detectar si una sola fila cambió. Necesito una forma eficiente de saber qué conjunto de filas cambió desde un momento determinado. Además, ¿es posible perder una actualización?
Aactualiza una fila, su versión de la fila cambia a 123, Aaún no se ha confirmado. time = 2: la transacción Bactualiza otra fila, su versión de fila cambia a 124. time = 3: Bcommits. time = 4: el proceso de sincronización se ejecuta y recupera todas las filas con rowversion> 122, lo que significa que las filas se actualizan solo por B. tiempo = 5: Aconfirmaciones. Resultado: los cambios por Anunca serán recogidos por el proceso de sincronización. ¿Me equivoco? Tal vez algún uso inteligente de MIN_ACTIVE_ROWVERSIONayudará?