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?
fuente

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ónBactualiza 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 porB. tiempo = 5:Aconfirmaciones. Resultado: los cambios porAnunca serán recogidos por el proceso de sincronización. ¿Me equivoco? Tal vez algún uso inteligente deMIN_ACTIVE_ROWVERSIONayudará?Respuestas:
Puedes usar una
ROWVERSIONcolumna para esto.La documentación indica que
Los valores son
BINARY(8)y debe considerarlos comoBINARYalgo más queBIGINTdespués de0x7FFFFFFFFFFFFFFFque continúe0x80...y comience a funcionar-9223372036854775808si se trata como un signobigint.Un ejemplo completo trabajado está por debajo. Mantener el índice en la
ROWVERSIONcolumna será costoso si tiene muchas actualizaciones, por lo que es posible que desee probar su carga de trabajo con y sin ver si vale la pena el costo.fuente
@@DBTSdebería haberMIN_ACTIVE_ROWVERSION(), y si se usa laMIN_ACTIVE_ROWVERSION()comparación,<=debería convertirse<y>convertirse>=.@@DBTSyMIN_ACTIVE_ROWVERSION()si hay transacciones activas no comprometidas. Si una aplicación usa en@@DBTSlugar deMIN_ACTIVE_ROWVERSION, es posible perder los cambios que están activos cuando se produce la sincronización.¿Has intentado usar la
IDENTITYopción?Por ejemplo:
dónde
Esto es similar a SECUENCIA en Oracle.
fuente
IDENTITYno hace lo que se requiere con respecto al incremento automático tanto en las actualizaciones como en las inserciones .IDENTITYpuede ayudar.