Afin de répondre correctement à cette question, vous devez d'abord décider: Que signifie «supprimer» dans le contexte de ce système / application?
Pour répondre à cette question, vous devez répondre à une autre question: pourquoi les enregistrements sont-ils supprimés?
Il existe plusieurs bonnes raisons pour lesquelles un utilisateur peut avoir besoin de supprimer des données. Habituellement, je trouve qu'il y a exactement une raison (par table) pour laquelle une suppression peut être nécessaire. Quelques exemples sont:
- Pour récupérer de l'espace disque;
- Suppression définitive requise conformément à la politique de conservation / confidentialité;
- Données corrompues / désespérément incorrectes, plus faciles à supprimer et à régénérer qu'à réparer.
- La majorité des lignes sont supprimées, par exemple une table de journal limitée à X enregistrements / jours.
Il existe également de très mauvaises raisons pour la suppression définitive (plus d'informations sur ces raisons plus tard):
- Pour corriger une erreur mineure. Cela souligne généralement la paresse des développeurs et une interface utilisateur hostile.
- Pour «annuler» une transaction (par exemple, une facture qui n'aurait jamais dû être facturée).
- Parce que vous le pouvez .
Pourquoi, demandez-vous, est-ce vraiment un gros problème? Quel est le problème avec un bon vieux DELETE
?
- Dans n'importe quel système, même à distance lié à l'argent, la suppression matérielle viole toutes sortes d'attentes comptables, même si elle est déplacée vers une table d'archive / pierre tombale. La bonne façon de gérer cela est un événement rétroactif .
- Les tables d'archivage ont tendance à diverger du schéma en direct. Si vous oubliez même une colonne ou une cascade nouvellement ajoutée, vous venez de perdre définitivement ces données.
- La suppression matérielle peut être une opération très coûteuse, en particulier avec les cascades . Beaucoup de gens ne se rendent pas compte que la cascade de plusieurs niveaux (ou dans certains cas toute cascade, selon le SGBD) entraînera des opérations au niveau de l'enregistrement au lieu d'opérations définies.
- La suppression répétée et fréquente accélère le processus de fragmentation de l'index.
Donc, la suppression logicielle est meilleure, non? Non, pas vraiment:
- La mise en place de cascades devient extrêmement difficile. Vous vous retrouvez presque toujours avec ce qui apparaît au client comme des lignes orphelines.
- Vous ne pouvez suivre qu'une seule suppression. Que faire si la ligne est supprimée et annulée plusieurs fois?
- Les performances de lecture en souffrent, bien que cela puisse être quelque peu atténué par le partitionnement, les vues et / ou les index filtrés.
- Comme indiqué précédemment, il peut en fait être illégal dans certains scénarios / juridictions.
La vérité est que ces deux approches sont fausses. La suppression est incorrecte. Si vous posez réellement cette question, cela signifie que vous modélisez l'état actuel au lieu des transactions. C'est une mauvaise, mauvaise pratique dans le domaine des bases de données.
Udi Dahan a écrit à ce sujet dans Don't Delete - Just Don't . Il y a toujours une sorte de tâche, opération, activité , ou (mon terme préféré) événement qui représente en fait la « suppression ». C'est OK si vous souhaitez par la suite dénormaliser dans une table "état actuel" pour les performances, mais faites-le après avoir cloué le modèle transactionnel, pas avant.
Dans ce cas, vous avez des "utilisateurs". Les utilisateurs sont essentiellement des clients. Les clients ont une relation commerciale avec vous. Cette relation ne disparaît pas simplement dans l'air, car ils ont annulé leur compte. Ce qui se passe vraiment, c'est:
- Le client crée un compte
- Le client annule le compte
- Le client renouvelle son compte
- Le client annule le compte
- ...
Dans tous les cas, c'est le même client , et éventuellement le même compte (ie chaque renouvellement de compte est un nouvel accord de service). Alors pourquoi supprimez-vous des lignes? C'est très facile à modéliser:
+-----------+ +-------------+ +-----------------+
| Account | --->* | Agreement | --->* | AgreementStatus |
+-----------+ +-------------+ +----------------+
| Id | | Id | | AgreementId |
| Name | | AccountId | | EffectiveDate |
| Email | | ... | | StatusCode |
+-----------+ +-------------+ +-----------------+
C'est ça. C'est tout ce qu'on peut en dire. Vous n'avez jamais besoin de supprimer quoi que ce soit. Ce qui précède est une conception assez courante qui permet un bon degré de flexibilité mais vous pouvez le simplifier un peu; vous pouvez décider que vous n'avez pas besoin du niveau "Accord" et que "Compte" doit simplement aller dans un tableau "AccountStatus".
Si un besoin fréquent dans votre application est d'obtenir une liste des accords / comptes actifs , il s'agit d'une requête (légèrement) délicate, mais c'est à cela que servent les vues:
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
)
Et tu as fini. Maintenant, vous avez quelque chose avec tous les avantages des suppressions en douceur, mais aucun des inconvénients:
- Les enregistrements orphelins ne posent aucun problème car tous les enregistrements sont visibles à tout moment; vous choisissez simplement d'une vue différente chaque fois que nécessaire.
- "Supprimer" est généralement une opération incroyablement bon marché - il suffit d'insérer une ligne dans une table d'événements.
- Il n'y a jamais aucune chance de perdre une histoire, jamais , peu importe à quel point vous vous trompez.
- Vous pouvez toujours supprimer définitivement un compte si vous en avez besoin (par exemple pour des raisons de confidentialité), et être à l'aise avec la certitude que la suppression se fera proprement et n'interférera avec aucune autre partie de l'application / base de données.
Le seul problème qui reste à résoudre est celui des performances. Dans de nombreux cas, il s'avère que ce n'est pas un problème en raison de l'index clusterisé AgreementStatus (AgreementId, EffectiveDate)
- il y a très peu d'E / S cherchant à s'y produire . Mais s'il s'agit d'un problème, il existe des moyens de le résoudre, en utilisant des déclencheurs, des vues indexées / matérialisées, des événements au niveau de l'application, etc.
Ne vous inquiétez pas trop tôt des performances - il est plus important de bien concevoir, et dans ce cas, «bien» signifie utiliser la base de données comme une base de données est censée être utilisée, en tant que système transactionnel .