Para responder adecuadamente a esta pregunta, primero debe decidir: ¿Qué significa "eliminar" en el contexto de este sistema / aplicación?
Para responder a esa pregunta, debe responder otra pregunta más: ¿por qué se eliminan los registros?
Existen varias buenas razones por las cuales un usuario podría necesitar eliminar datos. Por lo general, encuentro que hay exactamente una razón (por tabla) por la cual una eliminación podría ser necesaria. Algunos ejemplos son:
- Para reclamar espacio en disco;
- Se requiere eliminación de acuerdo con la política de retención / privacidad;
- Datos corruptos / irremediablemente incorrectos, más fáciles de eliminar y regenerar que de reparar.
- La mayoría de las filas se eliminan, por ejemplo, una tabla de registro limitada a X registros / días.
También hay algunas razones muy pobres para la eliminación forzada (más sobre las razones para esto más adelante):
- Para corregir un error menor. Esto generalmente subraya la pereza del desarrollador y una interfaz de usuario hostil.
- Para "anular" una transacción (por ejemplo, una factura que nunca debería haberse facturado).
- Porque tu puedes .
¿Por qué, preguntas, es realmente tan importante? ¿Qué hay de malo con el bueno DELETE
?
- En cualquier sistema, incluso vinculado remotamente al dinero, la eliminación dura viola todo tipo de expectativas contables, incluso si se traslada a una tabla de archivo / lápida. La forma correcta de manejar esto es un evento retroactivo .
- Las tablas de archivo tienden a diferir del esquema en vivo. Si olvida incluso una columna o cascada recién agregada, acaba de perder esos datos de forma permanente.
- La eliminación dura puede ser una operación muy costosa, especialmente con cascadas . Mucha gente no se da cuenta de que en cascada más de un nivel (o en algunos casos cualquier cascada, dependiendo de DBMS) dará lugar a operaciones a nivel de registro en lugar de operaciones de conjuntos.
- La eliminación frecuente y repetida acelera el proceso de fragmentación del índice.
Entonces, la eliminación suave es mejor, ¿verdad? No en realidad no:
- Configurar cascadas se vuelve extremadamente difícil. Casi siempre terminas con lo que le parece al cliente como filas huérfanas.
- Solo puedes rastrear una eliminación. ¿Qué pasa si la fila se elimina y se recupera varias veces?
- El rendimiento de lectura sufre, aunque esto puede mitigarse un poco con particiones, vistas y / o índices filtrados.
- Como se indicó anteriormente, en realidad puede ser ilegal en algunos escenarios / jurisdicciones.
La verdad es que ambos enfoques están equivocados. Eliminar está mal. Si realmente está haciendo esta pregunta, significa que está modelando el estado actual en lugar de las transacciones. Esta es una mala, mala práctica en tierra de bases de datos.
Udi Dahan escribió sobre esto en Don't Delete - Just Don't . Hay siempre algún tipo de tarea, transacción, la actividad , o (mi término preferido) evento que en realidad representa el "borrado". Está bien si posteriormente desea desnormalizar en una tabla de "estado actual" para el rendimiento, pero hágalo después de haber definido el modelo transaccional, no antes.
En este caso tienes "usuarios". Los usuarios son esencialmente clientes. Los clientes tienen una relación comercial con usted. Esa relación no se desvanece simplemente porque cancelaron su cuenta. Lo que realmente está sucediendo es:
- El cliente crea una cuenta
- El cliente cancela la cuenta
- El cliente renueva la cuenta
- El cliente cancela la cuenta
- ...
En todos los casos, es el mismo cliente y posiblemente la misma cuenta (es decir, cada renovación de cuenta es un nuevo acuerdo de servicio). Entonces, ¿por qué estás eliminando filas? Esto es muy fácil de modelar:
+-----------+ +-------------+ +-----------------+
| Account | --->* | Agreement | --->* | AgreementStatus |
+-----------+ +-------------+ +----------------+
| Id | | Id | | AgreementId |
| Name | | AccountId | | EffectiveDate |
| Email | | ... | | StatusCode |
+-----------+ +-------------+ +-----------------+
Eso es. Eso es todo al respecto. Nunca necesitas borrar nada. Lo anterior es un diseño bastante común que se adapta a un buen grado de flexibilidad, pero puede simplificarlo un poco; puede decidir que no necesita el nivel "Acuerdo" y simplemente hacer que "Cuenta" vaya a una tabla "Estado de cuenta".
Si una necesidad frecuente en su aplicación es obtener una lista de acuerdos / cuentas activas , entonces es una consulta (ligeramente) difícil, pero para eso están las vistas:
CREATE VIEW ActiveAgreements AS
SELECT agg.Id, agg.AccountId, acc.Name, acc.Email, s.EffectiveDate, ...
FROM AgreementStatus s
INNER JOIN Agreement agg
ON agg.Id = s.AgreementId
INNER JOIN Account acc
ON acc.Id = agg.AccountId
WHERE s.StatusCode = 'ACTIVE'
AND NOT EXISTS
(
SELECT 1
FROM AgreementStatus so
WHERE so.AgreementId = s.AgreementId
AND so.EffectiveDate > s.EffectiveDate
)
Y tu estas listo. Ahora tiene algo con todos los beneficios de las eliminaciones suaves pero ninguno de los inconvenientes:
- Los registros huérfanos no son un problema porque todos los registros son visibles en todo momento; simplemente selecciona desde una vista diferente cuando sea necesario.
- "Eliminar" suele ser una operación increíblemente barata: solo inserta una fila en una tabla de eventos.
- Nunca hay ninguna posibilidad de perder ningún historial, nunca , no importa cuán mal lo arruines.
- Todavía puede eliminar una cuenta de forma rígida si lo necesita (por ejemplo, por razones de privacidad) y estar seguro de que la eliminación se realizará de manera limpia y no interferirá con ninguna otra parte de la aplicación / base de datos.
El único problema que queda por abordar es el problema de rendimiento. En muchos casos, en realidad resulta no ser un problema debido al índice agrupado activado AgreementStatus (AgreementId, EffectiveDate)
: hay muy poca búsqueda de E / S allí. Pero si alguna vez es un problema, hay formas de resolverlo, utilizando disparadores, vistas indexadas / materializadas, eventos a nivel de aplicación, etc.
Sin embargo, no se preocupe por el rendimiento demasiado pronto: es más importante hacer que el diseño sea correcto, y "correcto" en este caso significa usar la base de datos de la manera en que se debe usar una base de datos, como un sistema transaccional .