2009-03-09 5 views
3

Ich habe die folgende Tabelle:Wie erstelle ich eine Tabelleneinschränkung, um doppelte Werte in zwei Spalten zu verhindern?

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] 

Ein Teil der Daten in der Tabelle wie etwas aussieht:

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

ich sicher, um eine Einschränkung hinzufügen möchten, dass niemand hinzufügt oder Updates ein Datensatz, um IsActive = 1 zu haben, wenn es bereits einen Datensatz für die EntityIdNmb gibt, wobei IsActive = 1 ist.

Wie mache ich das?

+0

@Eric, wenn es die Zeit erlaubt, möchten Sie erklären, warum die Clustered Indexed View-Lösung tomfut und ich Ihre Anfrage nicht erfüllen? Ich halte CI Views für eine elegante Lösung für diese Art von Problemen. –

+0

@Lieven und @tomfut, ich habe einen Trigger dafür erstellt, aber ich werde auch deine Lösung versuchen, da ich deine Antwort zuerst falsch verstanden habe. Danke. –

Antwort

6

Wenn Sie SQLServer verwenden, können Sie eine gruppierte indizierte Sicht erstellen.

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) 

Dies stellt sicher, es mit IsActive in der Tabelle nur ein EntityIdNmb ist = 1.

+0

Dies wird nicht funktionieren. Ich muss alle Datensätze zurückgeben, nicht nur Datensätze, bei denen IsActive = 1 ist. –

+0

@Eric, hast du mich wirklich dafür abgelehnt, ohne es überhaupt auszuprobieren? Es tut, was Sie fragen "Niemand fügt hinzu oder aktualisiert einen Datensatz, um IsActive = 1 zu haben, wenn es bereits einen Datensatz für die EntityIdNmb gibt, wo IsActive = 1 ist". –

+0

ich entschuldige mich. Ich habe den Downvote entfernt. Ich habe deine Antwort missverstanden. Ich dachte, du sagtest mir, ich solle eine Ansicht verwenden, um meine Daten abzurufen. –

3

Klingt so, als müssten Sie einen Trigger implementieren (vorausgesetzt, Ihr db-Produkt unterstützt dies). Wenn Sie nur einen aktiven und einen inaktiven Eintrag wünschen, funktioniert ein eindeutiger Index. Andernfalls müssen Sie eine Art benutzerdefinierte Einschränkung oder einen Trigger schreiben (wahrscheinlich 2 - 1 für Einfügungen, einer für Aktualisierungen), der sicherstellt, dass Sie nicht 2 Datensätze mit derselben ID haben, in der beide aktiv sind.

+0

könnten Sie mehr Einblick in das, was in meinem Trigger schreiben? mache ich einen Rollback oder was? –

+0

Eric, wenn Sie feststellen, dass es einen Konflikt gibt, werfen Sie eine Ausnahme. Ein Trigger ist nicht viel anders als ein gespeicherter proc - Sie registrieren ihn einfach anders, so dass die db weiß, wann sie ausgeführt wird. –

0

Wofür werden die inaktiven Datensätze verwendet? Sind sie ungenutzt und einfach da, um vorher aktive Aufzeichnungen zu verfolgen? Wenn ja, wäre es möglich, die Daten in mehrere Tabellen aufzuteilen? So etwas wie ...

EntityAttributeRelship (IDNmb, EntityIDNmb, AttributeIDNmb)

EntityAttributeRelshipHistory (IDNmb, EntityIDNmb, AttributeIDNmb)

Wenn es in der EntityAttributeRelship Tabelle ist, ist es aktiv ist. Wenn es in der History-Tabelle ist, wurde es irgendwann aktiviert und wurde seitdem deaktiviert?

Wenn Sie alles in einer Tabelle benötigen, würde ich mit todd.run Vorschlag eines Triggers gehen.

+0

Ich brauche alles in einer Tabelle. Es ist eine existierende Tabelle und die Daten sind schon da. Ich muss nur eine Einschränkung hinzufügen. Wenn ich einen Auslöser brauche, ist das verständlich, ich muss nur wissen, was ich schreiben soll. –

2

Wenn Sie MSSQL verwenden (ich glaube, das ist, was Ihre Syntax aussieht), erstellen nur die Zeilen einschließlich Ansicht mit IsActive = 1, dann fügen Sie einen eindeutigen Index für EntityIdNmb in der Ansicht ein.

In PostgreSQL, die ich habe mehr mit kurzem gearbeitet haben, können Sie einen Teilindex erstellen: http://www.postgresql.org/docs/8.3/interactive/indexes-partial.html

+0

@tomfut, Sie scheinen die gleiche Idee zu haben wie ich. Aus dem einen oder anderen Grund genügt es nicht dem Antrag des OP. –

+0

@Lieven, und deine Antwort war zuerst und detaillierter! Sorry für den Spam. Belair scheint Ihre Antwort zu nehmen, um zu implizieren, dass er dann seine SELEKTEN aus der Sicht tun sollte, nicht die ursprüngliche Tabelle. –

+0

@tomfut, kein Schaden angerichtet. Vielleicht haben Sie recht, wenn Sie davon ausgehen, aus der Sicht auszuwählen. @ Belair, wenn Sie dies lesen sollten, ist diese Annahme nicht wahr. Einfach auswählen, aktualisieren und in Ihre ursprüngliche Tabelle einfügen. Die CI-Ansicht wird sich um die Einschränkung kümmern. –

1

Die Sache mit den Trigger schreiben, ist zu entscheiden, ob Sie den Datensatz verwerfen wollen, um den Wert ändern 0 statt eins oder aktualisiere den alten Datensatz auf Null und lass diesen als einen stehen. Wenn Sie den Datensatz mit dem Wert 1 löschen, müssen Sie einen anderen Datensatz als aktiven Datensatz ändern, wie wählen Sie den Datensatz aus? Sobald Sie festlegen können, was Sie innerhalb des Triggers tun möchten, können wir Ihnen helfen, den Prozess besser zu gestalten.

Wir machen die letzten beiden Schritte, um jede Adresse zur Hauptpostadresse in unserer Datenbank zu machen. Unsere Geschäftsregel ist eine und nur eine Adresse kann die Hauptadresse sein und wenn es irgendwelche Adressen gibt, muss eine als Hauptadresse markiert sein. Der Schlüssel zu dieser Art von Trigger ist, sich daran zu erinnern, dass Einfügungen/Aktualisierungen/Löschungen in Batches auftreten können (auch wenn dies nicht die Norm ist) und um sicherzustellen, dass der Trigger auf einer Set-Basis funktioniert. Als ich hier ankam, implementierten wir eine mehrreihige Verarbeitung durch einen Cursor, was zu einer schlechten Sache wurde, als ich 200.000 Adressen in einem Import aktualisieren musste.(Hinweis für Unerfahrene - verwenden Sie niemals einen Cursor in einem Trigger!)

Verwandte Themen