2009-03-09 4 views
3

Je le tableau suivant:Comment créer une contrainte de table pour empêcher la duplication de valeurs sur deux colonnes?

CREATE TABLE [dbo].[EntityAttributeRelship](
    [IdNmb] [int] IDENTITY(1,1) NOT NULL, 
    [EntityIdNmb] [int] NOT NULL, 
    [AttributeIdNmb] [int] NOT NULL, 
    [IsActive] [bit] NOT NULL CONSTRAINT [DF_EntityAttributeRelship_IsActive] DEFAULT ((0)), 
CONSTRAINT [PK_EntityAttributeRelship] PRIMARY KEY CLUSTERED 
([IdNmb] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY] 

Une partie des données de la table ressemble à quelque chose ceci:

IdNmb EntityIdNmb AttributeIdNmb IsActive 
1  22    7    0 
2  22    8    0 
3  22    9    0 
4  22    10    1 

Je veux ajouter une contrainte pour vous assurer que personne n'ajoute ou des mises à jour un enregistrement ayant IsActive = 1, s'il existe déjà un enregistrement pour EntityIdNmb où IsActive = 1.

Comment procéder?

+0

@Eric, si le temps le permet, voudriez-vous expliquer pourquoi la solution de vue indexée en cluster tomfut et moi présentée ne répond pas à votre demande? Je considère CI Views comme une solution élégante à ce genre de problèmes. –

+0

@Lieven et @tomfut, j'ai créé un déclencheur pour faire cela, mais je vais essayer votre solution, car j'ai mal compris votre réponse au début. Je vous remercie. –

Répondre

6

Si vous utilisez SQLServer, vous pouvez créer une vue indexée en cluster.

CREATE VIEW dbo.VIEW_EntityAttributeRelship WITH SCHEMABINDING AS 
SELECT EntityIdNmb 
FROM dbo.EntityAttributeRelship 
WHERE IsActive = 1 
GO 

CREATE UNIQUE CLUSTERED INDEX UIX_VIEW_ENTITYATTRIBUTERELSHIP 
    ON dbo.VIEW_EntityAttributeRelship (EntityIdNmb) 

Ceci assure, il n'y a qu'une seule EntityIdNmb dans votre table avec IsActive = 1.

+0

Cela ne fonctionnera pas. J'ai besoin de retourner tous les enregistrements, pas seulement les enregistrements où IsActive = 1. –

+0

@ Eric, avez-vous vraiment downvoted moi pour cela sans même l'essayer? Il fait ce que vous demandez "personne n'ajoute ou met à jour un enregistrement pour avoir IsActive = 1, s'il y a déjà un enregistrement pour l'EntityIdNmb où IsActive = 1". –

+0

Je m'excuse. j'ai enlevé le downvote. j'ai mal compris votre réponse. Je pensais que vous me disiez d'utiliser une vue pour récupérer mes données. –

3

Il semble que vous ayez besoin d'implémenter un déclencheur (en supposant que votre produit db le supporte). Si vous ne voulez qu'une entrée active et une entrée inactive, un index unique fonctionnera. Sinon, vous devrez écrire une sorte de contrainte personnalisée ou un déclencheur (probablement 2 - une pour les insertions, une pour les mises à jour) qui s'assure que vous n'avez pas 2 enregistrements avec le même id où les deux sont actifs.

+0

pourriez-vous donner plus d'informations sur ce qu'il faut écrire dans mon déclencheur? puis-je effectuer une restauration ou quoi? –

+0

Eric, si vous trouvez qu'il y a un conflit, lancez une exception. Un déclencheur n'est pas très différent d'un proc stocké - vous l'inscrivez simplement différemment afin que la base de données sache quand l'exécuter. –

0

À quoi serviront les enregistrements inactifs? Sont-ils inutilisés et simplement là pour garder la trace des enregistrements précédemment actifs? Si oui, serait-il possible de diviser les données en plusieurs tables? Quelque chose comme ...

EntityAttributeRelship (IDNmb, EntityIDNmb, AttributeIDNmb)

EntityAttributeRelshipHistory (IDNmb, EntityIDNmb, AttributeIDNmb)

Si elle est dans la table EntityAttributeRelship, il est actif. Si c'est dans la table d'historique, alors il a été activé à un moment donné et a été désactivé depuis?

Si vous avez besoin de tout dans une table, je voudrais aller avec la suggestion de todd.run d'utiliser un déclencheur.

+0

J'ai besoin de tout dans une table. C'est une table existante et les données sont déjà là. J'ai juste besoin d'ajouter une contrainte. Si j'ai besoin d'un déclencheur, c'est compréhensible, j'ai juste besoin de savoir quoi écrire. –

2

Si vous utilisez MSSQL (je pense que c'est ce que votre syntaxe ressemble), créez une vue comprenant uniquement les lignes avec IsActive = 1, puis placez un index unique sur EntityIdNmb dans la vue.

Dans PostgreSQL, que j'ai travaillé plus récemment avec, vous pouvez créer un index partiel: http://www.postgresql.org/docs/8.3/interactive/indexes-partial.html

+0

@tomfut, vous semblez avoir la même idée que moi. Pour une raison ou une autre, il ne répond pas à la demande du PO. –

+0

@Lieven, et votre réponse a été la première et plus détaillée! Désolé pour le spam. Belair semble prendre votre réponse pour impliquer qu'il devrait alors faire ses SELECTS à partir de la vue, pas de la table d'origine. –

+0

@tomfut, aucun mal fait. Peut-être avez-vous raison de choisir dans la vue. @Belair, si vous deviez lire ceci, cette hypothèse n'est pas vraie. Il suffit de sélectionner, mettre à jour et insérer dans votre tableau d'origine. La vue CI prendra soin de la contrainte. –

1

La chose avec l'écriture du déclencheur est de décider si vous souhaitez rejeter l'enregistrement, changer la valeur 0 au lieu d'un ou mettre à jour l'ancien enregistrement à zéro et laisser celui-ci comme un seul. Si vous supprimez l'enregistrement avec la valeur 1, avez-vous besoin de changer un autre enregistrement pour qu'il soit actif, comment choisir lequel? Une fois que vous pouvez définir le waht que vous voulez faire dans le déclencheur, nous pouvons vous aider à mieux concevoir le processus.

Nous effectuons ces deux dernières étapes pour faire de n'importe quelle adresse l'adresse postale principale de notre base de données. Notre règle d'affaires est une et une seule adresse peut être la principale et s'il y a des adresses, il faut indiquer la principale. La clé de ce type de déclencheur est de se souvenir que les insertions/mises à jour/suppressions peuvent se produire par lots (même si ce n'est pas la norme) et de s'assurer que le déclencheur fonctionne de manière fixe. Quand je suis arrivé ici, le nôtre mettait en œuvre un traitement multi-lignes à l'aide d'un curseur qui devenait une mauvaise chose lorsque je devais mettre à jour 200 000 adresses dans une importation.(Note aux inexpérimentés - n'utilisez jamais un curseur dans un déclencheur!)

Questions connexes