2012-08-09 127 views
17

Tengo 2 tablas, Table-A y Table-A-History.Cómo almacenar registros históricos en una tabla de historial en SQL Server

  • Table-A contiene filas de datos actuales.
  • Table-A-History contiene datos históricos

me gustaría tener la fila más reciente de mis datos en Table-A y Table-A-History que contiene filas históricos.

puedo pensar en 2 maneras de lograr esto:

  1. cada vez que una nueva fila de datos está disponible, mover la fila actual del Table-A a Table-A-History y actualizar la fila Table-A con los últimos datos (a través de insert into select o select into table)

    o

  2. cada vez que una nueva fila se dispone de datos, actualizar Table-A 's fila e insertar una Nueva fila en Table-A-History.

En cuanto a rendimiento, ¿es mejor el método 1 o 2? ¿Hay una mejor manera diferente de lograr esto?

+2

¿Ha considerado utilizar triggers en 'Table-A' para crear las filas' Table-A-History' para usted? Asegúrese de que estén configurados para disparar al último ([sp_settriggerorder] (http://msdn.microsoft.com/en-us/library/ms186762.aspx)). – HABO

+0

No, no lo he hecho. Veré los factores desencadenantes. Gracias. – Mausimo

Respuesta

18

Los cambios en el registro son algo que generalmente he hecho al usar triggers en una tabla base para registrar los cambios en una tabla de registro. La tabla de registro tiene columnas adicionales para registrar el usuario de la base de datos, la acción y la fecha/hora.

create trigger Table-A_LogDelete on dbo.Table-A 
    for delete 
as 
    declare @Now as DateTime = GetDate() 
    set nocount on 
    insert into Table-A-History 
    select SUser_SName(), 'delete-deleted', @Now, * 
     from deleted 
go 
exec sp_settriggerorder @triggername = 'Table-A_LogDelete', @order = 'last', @stmttype = 'delete' 
go 
create trigger Table-A_LogInsert on dbo.Table-A 
    for insert 
as 
    declare @Now as DateTime = GetDate() 
    set nocount on 
    insert into Table-A-History 
    select SUser_SName(), 'insert-inserted', @Now, * 
     from inserted 
go 
exec sp_settriggerorder @triggername = 'Table-A_LogInsert', @order = 'last', @stmttype = 'insert' 
go 
create trigger Table-A_LogUpdate on dbo.Table-A 
    for update 
as 
    declare @Now as DateTime = GetDate() 
    set nocount on 
    insert into Table-A-History 
    select SUser_SName(), 'update-deleted', @Now, * 
     from deleted 
    insert into Table-A-History 
    select SUser_SName(), 'update-inserted', @Now, * 
     from inserted 
go 
exec sp_settriggerorder @triggername = 'Table-A_LogUpdate', @order = 'last', @stmttype = 'update' 

Los disparadores de registro siempre deben configurarse para disparar al último. De lo contrario, un desencadenante posterior puede revertir la transacción original, pero la tabla de registro ya se habrá actualizado. Esta es una situación confusa.

4

¿Qué tal el método 3: hacer Table-A una vista en contra de Table-A-History. Inserte en Table-A-History y deje que la lógica de filtrado apropiada genere Table-A. De esa manera solo estás insertando en una sola tabla.

+1

Pensé que debería separar las tablas, ya que Table-A contendrá ~ 10K registros que se utilizan con frecuencia. Donde la tabla de Historia se volverá enorme y se usará mucho menos. 5-10% del tiempo en comparación con la tabla-A. En cuanto al rendimiento, ¿no sería mejor tener la base de datos buscando a través de registros 10K con frecuencia, en lugar de una tabla grande si combiné la Tabla-A y la Tabla-A-Historia – Mausimo

+0

Posiblemente, posiblemente no, dependiendo de la proporción de inserciones a selectas. También podría hacer que Table-A sea una vista indizada (http://msdn.microsoft.com/en-us/library/dd171921%28v=sql.100%29.aspx). Esto podría resolver el problema de búsqueda para usted directamente. – mwigdahl

3

Aunque consume más espacio, tener la tabla de historial que contiene el registro más reciente también le ahorrará dolor al escribir informes y ver cómo se produjeron los cambios y cuándo. Algo que vale la pena pensar en mi opinión.

En cuanto al rendimiento, espero que sean idénticos. Pero, ciertamente, no le gustaría eliminar el registro (opción 1 de "mover") desde la tabla que no es hist porque está usando integridad referencial entre las dos tablas, ¿verdad?

+0

Correcto, actualizaría el registro en la tabla A. La tabla Table-A-History utiliza una clave sustituta + una clave externa que enlaza con la Tabla-A – Mausimo

2

yo preferiría el método 1
Además, voy a tener también mantener el registro actual en la tabla de historial también
que depende de la necesidad.

3

La opción 1 está bien. Pero tienes método también :) 4

  1. Insertar nuevo registro a su mesa,

  2. Mover antiguo registro para archivar tabla de la base de regular usando programador mysql. Puede programar la archivación de datos en el momento de carga mínima, por ejemplo, en horas nocturnas.

+1

Opps, lo siento. Pero la idea es la misma. En el caso de que no quiera perder el rendimiento durante el día, solo hágalo de noche ;-) – Vahan

+0

Creo que la pregunta no es solo acerca de los INSERTOS, sino también de ACTUALIZACIONES y DEPRESIONES. ¿Qué pasa si una fila insertada se actualiza o elimina el mismo día? En ese caso, no es posible rastrear los cambios que se realizan el mismo día. (Si la pregunta es solo acerca de INSERT, entonces no se necesita una tabla de auditoría, porque no se cambiarán los datos usando solo INSERT). – beawolf

36

Básicamente, usted está buscando rastrear/auditar los cambios en una tabla mientras mantiene la tabla principal pequeña.

Existen varias formas de solucionar este problema. Los inconvenientes y ventajas de cada camino se discuten a continuación.

1 - Comprobación de la tabla con activadores.

Si está buscando auditar la tabla (inserciones, actualizaciones, eliminaciones), mire cómo evitar transacciones no deseadas - SQL Saturday slide deck w/code - http://craftydba.com/?page_id=880. El activador que llena la tabla de auditoría puede contener información de varias tablas, si lo desea, ya que los datos se guardan como XML. Por lo tanto, puede eliminar una acción si es necesario analizando el XML. Realiza un seguimiento de quién y qué hizo el cambio.

Opcionalmente, puede tener la tabla de auditoría en su propio grupo de archivos.

Description: 
    Table Triggers For (Insert, Update, Delete) 
    Active table has current records. 
    Audit (history) table for non-active records. 

Pros: 
    Active table has smaller # of records. 
    Index in active table is small. 
    Change is quickly reported in audit table. 
    Tells you what change was made (ins, del, upd) 

Cons: 
    Have to join two tables to do historical reporting. 
    Does not track schema changes. 

2 - eficaz fechar los registros

Si nunca va a purgar los datos de la tabla de auditoría, por qué no marcar la fila como eliminados pero mantenerlo para siempre? Muchos sistemas, como las personas, usan de forma suave las citas efectivas para mostrar si un registro ya no está activo. En el mundo de BI esto se llama una tabla de tipo 2 dimensiones (dimensiones que cambian lentamente). Ver el artículo del instituto de almacenamiento de datos. http://www.bidw.org/datawarehousing/scd-type-2/ Cada registro tiene una fecha de inicio y finalización.

Todos los registros activos tienen una fecha de finalización nula.

Description: 
    Table Triggers For (Insert, Update, Delete) 
    Main table has both active and historical records. 

Pros: 
    Historical reporting is easy. 
    Change is quickly shown in main table. 

Cons: 
    Main table has a large # of records. 
    Index of main table is large. 
    Both active & history records in same filegroup. 
    Does not tell you what change was made (ins, del, upd) 
    Does not track schema changes. 

3 - captura de datos modificados (Característica de la empresa).

Micorsoft SQL Server 2008 introdujo la función de captura de datos modificados. Mientras esto rastrea el cambio de datos (CDC) usando un lector de LOG después del hecho, le faltan cosas como quién y qué hizo el cambio. Detalles de MSDN: http://technet.microsoft.com/en-us/library/bb522489(v=sql.105).aspx

Esta solución depende de los trabajos de CDC en ejecución. Cualquier problema con el agente sql provocará retrasos en la presentación de datos.

Consulte las tablas de cambio de captura de datos. http://technet.microsoft.com/en-us/library/bb500353(v=sql.105).aspx

Description: 
    Enable change data capture 

Pros: 
    Do not need to add triggers or tables to capture data. 
    Tells you what change was made (ins, del, upd) the _$operation field in 
    <user_defined_table_CT> 
    Tracks schema changes.  

Cons: 
    Only available in enterprise version. 
    Since it reads the log after the fact, time delay in data showing up. 
    The CDC tables do not track who or what made the change. 
    Disabling CDC removes the tables (not nice)! 
    Need to decode and use the _$update_mask to figure out what columns changed. 

4 - seguimiento de cambios de funciones (todas las versiones).

Micorsoft SQL Server 2008 introdujo la función de seguimiento de cambios. A diferencia de CDC, viene con todas las versiones; Sin embargo, viene con un conjunto de funciones TSQL que debes llamar para descubrir qué sucedió.

Fue diseñado con el propósito de sincronizar una fuente de datos con el servidor SQL a través de una aplicación. Hay un marco de sincronización completo que funciona en TechNet.

http://msdn.microsoft.com/en-us/library/bb933874.aspx http://msdn.microsoft.com/en-us/library/bb933994.aspx http://technet.microsoft.com/en-us/library/bb934145(v=sql.105).aspx

A diferencia de los CDC, se especifica cómo los cambios largos últimos en la base de datos antes de ser purgado. Además, las inserciones y eliminaciones no registran datos. Las actualizaciones solo registran qué campo cambió.

Dado que está sincronizando el origen del servidor SQL con otro destino, funciona bien. No es bueno auditar a menos que escriba un trabajo periódico para descubrir los cambios.

Aún tendrá que almacenar esa información en algún lugar.

Description: 
    Enable change tracking 

Cons: 
    Not a good auditing solution 

Las primeras tres soluciones funcionarán para su auditoría. Me gusta la primera solución ya que la uso ampliamente en mi entorno.

Sinceramente

John

Fragmento de código de la presentación (Autos de base de datos)

-- 
-- 7 - Auditing data changes (table for DML trigger) 
-- 


-- Delete existing table 
IF OBJECT_ID('[AUDIT].[LOG_TABLE_CHANGES]') IS NOT NULL 
    DROP TABLE [AUDIT].[LOG_TABLE_CHANGES] 
GO 


-- Add the table 
CREATE TABLE [AUDIT].[LOG_TABLE_CHANGES] 
(
    [CHG_ID] [numeric](18, 0) IDENTITY(1,1) NOT NULL, 
    [CHG_DATE] [datetime] NOT NULL, 
    [CHG_TYPE] [varchar](20) NOT NULL, 
    [CHG_BY] [nvarchar](256) NOT NULL, 
    [APP_NAME] [nvarchar](128) NOT NULL, 
    [HOST_NAME] [nvarchar](128) NOT NULL, 
    [SCHEMA_NAME] [sysname] NOT NULL, 
    [OBJECT_NAME] [sysname] NOT NULL, 
    [XML_RECSET] [xml] NULL, 
CONSTRAINT [PK_LTC_CHG_ID] PRIMARY KEY CLUSTERED ([CHG_ID] ASC) 
) ON [PRIMARY] 
GO 

-- Add defaults for key information 
ALTER TABLE [AUDIT].[LOG_TABLE_CHANGES] ADD CONSTRAINT [DF_LTC_CHG_DATE] DEFAULT (getdate()) FOR [CHG_DATE]; 
ALTER TABLE [AUDIT].[LOG_TABLE_CHANGES] ADD CONSTRAINT [DF_LTC_CHG_TYPE] DEFAULT ('') FOR [CHG_TYPE]; 
ALTER TABLE [AUDIT].[LOG_TABLE_CHANGES] ADD CONSTRAINT [DF_LTC_CHG_BY] DEFAULT (coalesce(suser_sname(),'?')) FOR [CHG_BY]; 
ALTER TABLE [AUDIT].[LOG_TABLE_CHANGES] ADD CONSTRAINT [DF_LTC_APP_NAME] DEFAULT (coalesce(app_name(),'?')) FOR [APP_NAME]; 
ALTER TABLE [AUDIT].[LOG_TABLE_CHANGES] ADD CONSTRAINT [DF_LTC_HOST_NAME] DEFAULT (coalesce(host_name(),'?')) FOR [HOST_NAME]; 
GO 



-- 
-- 8 - Make DML trigger to capture changes 
-- 


-- Delete existing trigger 
IF OBJECT_ID('[ACTIVE].[TRG_FLUID_DATA]') IS NOT NULL 
    DROP TRIGGER [ACTIVE].[TRG_FLUID_DATA] 
GO 

-- Add trigger to log all changes 
CREATE TRIGGER [ACTIVE].[TRG_FLUID_DATA] ON [ACTIVE].[CARS_BY_COUNTRY] 
    FOR INSERT, UPDATE, DELETE AS 
BEGIN 

    -- Detect inserts 
    IF EXISTS (select * from inserted) AND NOT EXISTS (select * from deleted) 
    BEGIN 
    INSERT [AUDIT].[LOG_TABLE_CHANGES] ([CHG_TYPE], [SCHEMA_NAME], [OBJECT_NAME], [XML_RECSET]) 
    SELECT 'INSERT', '[ACTIVE]', '[CARS_BY_COUNTRY]', (SELECT * FROM inserted as Record for xml auto, elements , root('RecordSet'), type) 
    RETURN; 
    END 

    -- Detect deletes 
    IF EXISTS (select * from deleted) AND NOT EXISTS (select * from inserted) 
    BEGIN 
    INSERT [AUDIT].[LOG_TABLE_CHANGES] ([CHG_TYPE], [SCHEMA_NAME], [OBJECT_NAME], [XML_RECSET]) 
    SELECT 'DELETE', '[ACTIVE]', '[CARS_BY_COUNTRY]', (SELECT * FROM deleted as Record for xml auto, elements , root('RecordSet'), type) 
    RETURN; 
    END 

    -- Update inserts 
    IF EXISTS (select * from inserted) AND EXISTS (select * from deleted) 
    BEGIN 
    INSERT [AUDIT].[LOG_TABLE_CHANGES] ([CHG_TYPE], [SCHEMA_NAME], [OBJECT_NAME], [XML_RECSET]) 
    SELECT 'UPDATE', '[ACTIVE]', '[CARS_BY_COUNTRY]', (SELECT * FROM deleted as Record for xml auto, elements , root('RecordSet'), type) 
    RETURN; 
    END 

END; 
GO 



-- 
-- 9 - Test DML trigger by updating, deleting and inserting data 
-- 

-- Execute an update 
UPDATE [ACTIVE].[CARS_BY_COUNTRY] 
SET COUNTRY_NAME = 'Czech Republic' 
WHERE COUNTRY_ID = 8 
GO 

-- Remove all data 
DELETE FROM [ACTIVE].[CARS_BY_COUNTRY]; 
GO 

-- Execute the load 
EXECUTE [ACTIVE].[USP_LOAD_CARS_BY_COUNTRY]; 
GO 

-- Show the audit trail 
SELECT * FROM [AUDIT].[LOG_TABLE_CHANGES] 
GO 

-- Disable the trigger 
ALTER TABLE [ACTIVE].[CARS_BY_COUNTRY] DISABLE TRIGGER [TRG_FLUID_DATA]; 

** Mira & Sensación de la tabla de auditoría **

enter image description here

+0

¡Gran lectura, gracias por la información! Solo quiero verificar que entendí tu fragmento de código. Solo tiene 1 tabla "Cambios en la tabla de registro", que almacenará los registros de cada otra tabla y los registros reales de esas tablas se almacenan en XML. ¿De esa manera solo tienes una tabla de auditoría? – Mausimo

+0

Lo bueno es que es hasta usted 2.Digamos que su empresa tiene impuestos, su período de retención de datos es de 7 años. Es posible que desee utilizar varias tablas de auditoría de partición en chg_date. Vea mi presentación sobre las técnicas de almacenamiento de datos. Por otro lado, si vende helados para un negocio, puede conservar los recibos durante 2 años. Entonces, una mesa podría estar bien. –

+0

¿No deberían los "Contras" para los desencadenantes aumentar el tiempo de inserción/actualización/eliminación? Siento que parte de la decisión correcta aquí es equilibrar la velocidad de escritura frente a la velocidad de consulta. – Daniel

8

Las versiones recientes de SQL Server (2016+ y Azure) tienen tablas temporales que proporcionan la funcionalidad exacta solicitada, como una característica de primera clase. https://docs.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables

Alguien en Microsoft probablemente lea esta página. :)

+1

Gracias por agregar esto. Esta pregunta fue en realidad hace bastante tiempo. Casualmente, estoy trabajando en un nuevo proyecto que tiene un requisito similar y realmente estoy usando Azure. Estaré buscando en Tablas Temporales. ¡Aclamaciones! – Mausimo

Cuestiones relacionadas