Me gustaría implementar una función de "recuperación" en una aplicación web para que un usuario pueda cambiar de opinión y recuperar un registro eliminado. ¿Pensamientos sobre cómo implementar esto? Algunas opciones que he considerado son en realidad eliminar el registro en cuestión y almacenar los cambios en una tabla de auditoría separada, o no eliminar el registro y usar una columna booleana "eliminada" para marcarlo como eliminado. La última solución requeriría una lógica de aplicación adicional para ignorar los registros "eliminados" en circunstancias normales, pero facilitaría mucho la implementación de la recuperación de los registros en el lado de la aplicación.
database-design
delete
audit
Abie
fuente
fuente
Respuestas:
Sí, definitivamente elegiría la segunda opción, pero agregaría un campo más a un campo de fecha.
Entonces agregas:
Te dejaría un tiempo para la acción de recuperación.
Si el tiempo es inferior a una hora, se puede recuperar.
Para eliminar realmente la entrada eliminada, simplemente cree un procedimiento almacenado que limpiará cada entrada con la eliminación establecida en verdadero y el tiempo superior a una hora y póngalo como una pestaña cron que se ejecuta cada 24 horas
La hora es solo un ejemplo.
fuente
cleaned
, o algo así, que indique que los datos asociados con este registro se han eliminado de manera adecuada y completa. El registro puede recuperarse a menos quecleaned
sea verdadero, en cuyo caso es irrecuperable.deleted_at
contiene tanto la semántica deldelete
booleano como ladelete_date
marca de tiempo. Si sedeleted_at
trata deNULL
un caso, el casodelete
esFALSE
ydelete_date
esNULL
,deleted_at
contiene una marca de fecha y hora, el casodelete
esTRUE
ydelete_date
contiene una marca de tiempo, lo que le ahorra tiempo, almacenamiento y lógica de aplicación.deleted
campo puede mejorar enormemente el rendimiento cuando consulta filas no eliminadasEn nuestras aplicaciones no lo hacemos realmente nada de borrado en una solicitan los usuarios de todos modos (nuestros clientes están en entornos regulados en los que borrar nada puede potencialmente conducir a problemas legales).
Mantenemos las versiones anteriores en una tabla de auditoría separada (por lo que para la tabla some_table donde también hay una tabla llamada some_table_audit) que es idéntica aparte de tener un identificador de versión adicional (una marca de tiempo si su DB admite valores de tiempo lo suficientemente granulares, un número de versión entero) o UUID que es una clave foránea para una tabla de auditoría general, o así sucesivamente), y actualice la tabla de auditoría automáticamente por disparador (por lo que no es necesario que todo el código que actualiza los registros conozca el requisito de auditoría).
De esta manera:
Si usa una marca de tiempo en lugar de (o además de) un número de versión entero, puede usar esto para eliminar las copias más antiguas después de un período de tiempo establecido si es necesario. Pero el espacio en disco es relativamente barato en estos días, por lo que a menos que tengamos razones para descartar datos antiguos (es decir, regulaciones de protección de datos que dicen que debe eliminar los datos del cliente después de X meses / años), no lo haríamos.
Esta respuesta ha existido hace unos años y desde entonces un par de cosas clave que podrían afectar este tipo de planificación han cambiado. No entraré en detalles masivos, pero brevemente para el beneficio de las personas que leen esto hoy:
SQL Server 2016 introdujo "tablas temporales versionadas por el sistema" que hacen mucho de este trabajo por usted, y más además, ya que se proporciona un poco de azúcar sintáctica agradable para hacer que las consultas históricas sean más fáciles de construir y mantener, y coordinan un subconjunto de cambios de esquema entre tablas de base e historia. No carecen de advertencias, pero son una herramienta poderosa para este tipo de propósito. Características similares también están disponibles en otros sistemas de base de datos.
Los cambios en la legislación de protección de datos, en particular la introducción de GDPR, pueden alterar significativamente la cuestión de cuándo los datos deben eliminarse por completo. Debe sopesar el saldo de no eliminar datos que podrían ser útiles (o, de hecho, legalmente requeridos) para fines de auditoría en una fecha posterior en contra de la necesidad de respetar los derechos de las personas (tanto en general como específicamente establecido en la legislación pertinente) al considerar tus diseños Esto puede ser un problema con las tablas temporales con versión del sistema, ya que no puede modificar el historial para purgar los datos personales sin cambios de esquema a corto plazo para desactivar el seguimiento del historial mientras realiza cambios.
fuente
Con una columna borrada booleana, comenzará a tener problemas si su tabla comienza a crecer y se hace realmente grande. Le sugiero que mueva las columnas eliminadas una vez por semana (más o menos según sus especificaciones) a una tabla diferente. De esa manera, tiene una bonita y pequeña tabla activa y una grande que contiene todos los registros recopilados con el tiempo.
fuente
Yo iría con la mesa separada. Ruby on Rails tiene un
acts_as_versioned
complemento, que básicamente guarda una fila en otra tabla con el postfix_version
antes de actualizarlo. Si bien no necesita ese comportamiento exacto, también debería funcionar para su caso (copie antes de eliminar).Al igual que @Spredzy, también recomendaría agregar una
delete_date
columna para poder purgar mediante programación los registros que no se han restaurado después de X horas / días / lo que sea.fuente
La solución que utilizamos internamente para este asunto es tener una columna de estado con algunos valores codificados para algunos estados específicos del objeto: Eliminado, Activo, Inactivo, Abierto, Cerrado, Bloqueado: cada estado con algún significado utilizado en la aplicación. Desde el punto de vista de db, no eliminamos objetos, simplemente cambiamos el estado y mantenemos el historial de cada cambio en la tabla de objetos.
fuente
Cuando dice que "la última solución requeriría una lógica de aplicación adicional para ignorar los registros 'eliminados'", la solución simple es tener una vista que los filtre.
fuente
Similar a lo que sugirió Spredzy, utilizamos un campo de marca de tiempo para eliminarlo en todas nuestras aplicaciones. El booleano es superfluo, ya que la marca de tiempo que se establece indica que el registro se ha eliminado. De esta manera, nuestro PDO siempre agrega
AND (deleted IS NULL OR deleted = 0)
a las declaraciones select, a menos que el modelo solicite explícitamente que se incluyan registros eliminados.Actualmente no recolectamos basura en ninguna tabla excepto que contenga blobs o textos; el espacio es trivial si los registros están bien normalizados, y la indexación del
deleted
campo tiene un impacto limitado en la velocidad de selección.fuente
Alternativamente, puede colocar la responsabilidad en los usuarios (y desarrolladores) e ir con una secuencia de '¿Está seguro?', '¿Está definitivamente seguro?' y '¿Estás absolutamente, bien y verdaderamente seguro?' preguntas antes de que se elimine el registro. Ligeramente gracioso pero vale la pena considerarlo.
fuente
Estoy acostumbrado a ver filas de tablas con columnas como 'DeletedDate' en ellas y no me gustan. La noción misma de 'eliminado' es que la entrada no debería haberse hecho en primer lugar. Prácticamente, no se pueden eliminar de la base de datos, pero no los quiero con mis datos activos. Las filas eliminadas lógicamente son, por definición, datos fríos a menos que alguien específicamente quiera ver los datos eliminados.
Además, cada consulta que se escriba tiene que excluirlos específicamente y los índices también deben considerarlos.
Lo que me gustaría ver es un cambio en el nivel de arquitectura de la base de datos y el nivel de la aplicación: cree un esquema llamado 'eliminado'. Cada tabla definida por el usuario tiene un equivalente idéntico en el esquema 'eliminado' con un campo adicional que contiene metadatos: el usuario que lo eliminó y cuándo. Las claves foráneas requieren ser creadas.
A continuación, elimina se convierte en insertar-eliminar. Primero, la fila que se eliminará se inserta en su contraparte del esquema 'eliminado'. La fila en cuestión en la tabla principal se puede eliminar. Sin embargo, es necesario agregar lógica adicional en algún lugar a lo largo de la línea. Las infracciones de claves externas pueden ser manejadas.
Las claves foráneas deben manejarse adecuadamente. Es una mala práctica tener una fila eliminada lógicamente pero cuyo primario / único tiene columnas en otras tablas que se refieren a él. Esto no debería suceder de todos modos. Un trabajo normal puede eliminar filas de viudas (filas cuyas claves principales no tienen referencias en otras tablas a pesar de la presencia de una clave externa. Sin embargo, esto es lógica empresarial.
El beneficio general es la reducción de metadatos en la tabla y la mejora del rendimiento que aporta. La columna 'deletedDate' dice que esta fila en realidad no debería estar aquí, pero, por conveniencia, la dejamos allí y dejamos que la consulta SQL la maneje. Si una copia de la fila eliminada se mantiene en un esquema 'eliminado', entonces la tabla principal con los datos activos tiene un mayor porcentaje de datos activos (suponiendo que se archiven de manera oportuna) y menos columnas de metadatos innecesarios. Los índices y consultas ya no necesitan considerar este campo. Cuanto más corto sea el tamaño de la fila, más filas se pueden ajustar en una página, más rápido puede funcionar SQL Server.
La principal desventaja es el tamaño de la operación. Ahora hay dos operaciones en lugar de una, así como la lógica adicional y el manejo de errores. Puede llevar a más bloqueo que actualizar una sola columna, de lo contrario tomaría. La transacción mantiene bloqueos en la tabla por más tiempo y hay dos tablas involucradas. Eliminar datos de producción, al menos en mi experiencia, es algo que rara vez se hace. Aún así, en una de las tablas principales, el 7,5% de casi 100 millones de entradas tiene una entrada en la columna 'Fecha eliminada'.
Como respuesta a la pregunta, la aplicación debería ser consciente de 'recuperar'. Simplemente tendría que hacer lo mismo en orden inverso: inserte la fila del esquema 'eliminado' en la tabla principal y luego elimine la fila del esquema 'eliminado'. Nuevamente, se necesita algo más de lógica y manejo de errores para garantizar que se eviten errores, problemas con claves externas y similares.
fuente