2009-09-04 6 views
2

je dois expliquer par exemple:Base de données de conception préférence: L'utilisation d'un DateTime et un TBI dans SQL 2000

est-il une meilleure pratique ou la préférence pour spécifier un DateTime et BIT dans une table de base de données?

Dans ma base de données, j'ai une table Widget. J'ai besoin de savoir si un widget est "Fermé" et c'est "Date fermée" Les règles métier disent que si un widget est fermé, il doit avoir une date fermée. Si un widget n'est pas fermé, il ne doit pas avoir de "date de fermeture".

Pour concevoir, je pouvais faire ce qui suit:

(exemple 1):

CREATE TABLE [Widget] 
(
    [WidgetID] INT IDENTITY(1,1) 
    ,[ClosedDate] DATETIME NULL 
) 

ou (exemple 2):

CREATE TABLE [Widget] 
(
    [WidgetID] INT IDENTITY(1,1) 
    ,[IsClosed] BIT NOT NULL CONSTRAINT [DF_Widget_IsClosed] DEFAULT (0) 
    ,[ClosedDate] DATETIME NULL 
) 

Je pense que l'exemple 1 est plus propre parce que c'est une colonne de moins à s'inquiéter. Mais, chaque fois que j'ai besoin d'évaluer si un widget est fermé, j'aurais besoin d'une étape supplémentaire pour déterminer si la colonne ClosedDate n'est pas NULL.

L'exemple 2 crée un surcoût supplémentaire car je dois maintenant synchroniser les valeurs IsClosed et ClosedDate.

Existe-t-il une meilleure pratique lors de la conception de quelque chose comme ça? L'interrogation de la table serait-elle plus performante pour l'exemple 2? Y a-t-il une raison pour laquelle je devrais choisir un design plutôt qu'un autre?

Remarque: j'accède à cette valeur via un outil ORM ainsi que des procédures stockées.

+0

rendre le "IsClosed" une colonne calculée qui est vrai seulement lorsque ClosedDate n'est pas nulle - voir la réponse de JBrooks - vous donne le meilleur des deux scénarios! –

Répondre

5

Je pense que l'option 1 est meilleure. L'intégrité des données est mieux conservée (impossible d'avoir une date fermée avec un drapeau qui indique l'inverse), prend moins d'espace disque dans le cas de très grandes tables, et les requêtes seraient toujours performantes et claires pour les coéquipiers.

2

Le premier est meilleur. La vérification de null est bon marché, alors que garder un drapeau séparé permet d'avoir une date fermée mais pas encore fermée.

0

Je ne voudrais pas affecter une signification sémantique à NULL. Ce faisant, vous barboter dans votre logique métier et vous obtiendrez un code comme ...

public class Widget 
{ 
    // stuff 

    public bool IsClosed 
    { 
    // what do you put here? 
    // it was null in the db so you have to use DateTime.MinDate or some such. 
    return(_closeDate == ??); 
    } 

    // more stuff 
} 

Utiliser null de cette façon est mauvaise. NULL (et null) signifie "Je ne sais pas". Vous attribuez une signification sémantique à cette réponse alors qu'en réalité, vous ne devriez pas. Le statut fermé est le statut fermé et la date de clôture est la date de clôture, ne les combinez pas. (Dieu vous interdit de vouloir rouvrir un Widget, mais n'oubliez pas quand il a été fermé en premier lieu, par exemple.)

Eric Lippert a un nice blog post sur l'utilisation de null de cette façon (kidna) ainsi.

+0

Dans le C#, vous utiliseriez un DateTime? (Nullable ) pour correspondre au champ de base de données, c'est ce que vous feriez dans cette déclaration de retour. Je ne peux pas dire que je suis d'accord avec vos conseils, car cela va à l'encontre de DRY. –

+2

Je viens de lire le post de Lippert et je ne vois pas comment il supporte votre vue. Null signifie qu'aucune valeur n'existe, ce qui est différent de toute valeur existante, y compris vide. Dans ce cas, une date de fermeture nulle signifie qu'aucune date de fermeture n'existe car elle n'est pas fermée. Si vous vouliez le rouvrir et garder trace des dates de clôture précédentes, je vous suggère de le mettre dans un tableau d'historique. –

+0

Je suis également en désaccord. Et dans votre exemple, vous renverriez simplement _closeDate.HasValue() (en C#) – Shawson

2

Je pense que vous avez la colonne IsClosed comme colonne calculée.

CREATE TABLE [Widget]( 
[WidgetID] INT IDENTITY(1,1), 
[ClosedDate] DATETIME NULL, 
IsClosed AS CAST(CASE WHEN ClosedDate IS NULL THEN 0 ELSE 1 END AS BIT) 
) 

La raison est que vous n'êtes pas stocker quoi que ce soit et vous pouvez coder maintenant votre code d'application et stockées procs utiliser cette colonne. Si votre règle d'affaires change, vous pouvez la convertir en une vraie colonne et vous n'aurez pas besoin de changer d'autre code. Sinon, vous aurez une logique commerciale répartie dans tout le code de votre application et les procédures stockées. De cette façon, c'est seulement en 1 place.

Enfin, lorsque vous passez à SQL2005, vous pouvez ajouter la clause "Persisted". Ainsi, il sera stocké augmentant légèrement la performance et vous n'aurez aucun problème à les synchroniser.

+0

+1 parfait - mes pensées exactement! Une colonne avec laquelle vous travaillez et une colonne calculée (qui est automatiquement mise à jour) pour signaler la condition. –

+0

Ceci est un compromis intéressant. D'une part, même si elle n'est pas persistante, vous pouvez créer un index sur elle, qui le persiste effectivement. D'un autre côté, je suis assez à l'aise avec l'utilisation de null pour signifier null. –

Questions connexes