2012-11-10 7 views
7

Wenn ich eine Tabelle habenEinsetzen in eine Selbst Tabelle verwiesen

Table 
{ 
ID int primary key identity, 
ParentID int not null foreign key references Table(ID) 
} 

wie kommt ein Einsatz zunächst in einer Tabellenzeile?

Aus Sicht der Geschäftslogik sollte nicht NULL Constraint auf ParentID nicht gelöscht werden.

+1

Intersting Frage. Ist es überhaupt möglich, auf dich selbst zu verweisen? Wie können Sie sogar 1 Datensätze erstellen, wenn sie neu erstellt werden? – YvesR

+2

Welche Zeile ist die übergeordnete Zeile Ihrer ersten Zeile? –

+2

Für die erste Zeile sollte ParentID ID sein. Wenn ich mehrere Wurzeln in meiner Baumstruktur verwenden möchte, hat jeder Stamm ParentID == ID. Daher kann die Frage wie erweitert werden - wie kann ich Stammzeilen einfügen?Kann die gespeicherte Prozedur SCOPE_IDENTITY verwenden, um diese Einfügung vorzunehmen? – Nezreli

Antwort

5

In SQL Server, eine einfache INSERT tun:

create table dbo.Foo 
(
ID int primary key identity, 
ParentID int not null foreign key references foo(ID) 
) 
go 

insert dbo.Foo (parentId) values (1) 

select * from dbo.Foo 

Ergebnisse in

ID   ParentID 
----------- ----------- 
    1   1 

Wenn Sie einen Wert einzufügen sind versucht, die aus Ihrer Identität Samen anders sein wird, das Einfügen wird versagen.

UPDATE:

Die Frage ist nicht ganz klar, was der Kontext (dh der Code sollte in einem Live-Produktionssystem arbeiten oder einen DB-Setup-Skript nur) und aus den Kommentaren scheint es hart codierte Die ID ist möglicherweise keine Option. Der obige Code sollte normalerweise in den DB-Initialisierungsskripten funktionieren, in denen die Hierarchie-Stamm-ID bekannt und konstant sein muss. Im Fall einer Gesamtstruktur (mehrere Stämme mit IDs, die nicht im Voraus bekannt sind) sollte das Folgende wie vorgesehen funktionieren:

Dann könnte man die letzte Identität wie üblich abfragen (SCOPE_IDENTITY, etc.). Um Adresse @ usr Bedenken, ist der Code in der Tat transaktions sicher wie das folgende Beispiel zeigt:

insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo')) 
insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo')) 
insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo')) 

select * from dbo.Foo 

select IDENT_CURRENT('dbo.Foo') 
begin transaction 
    insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo')) 
    rollback 

select IDENT_CURRENT('dbo.Foo') 

insert dbo.Foo (parentId) values (IDENT_CURRENT('dbo.Foo')) 

select * from dbo.Foo 

Das Ergebnis:

ID   ParentID 
----------- ----------- 
1   1 
2   2 
3   3 

currentIdentity 
--------------------------------------- 
3 

currentIdentity 
--------------------------------------- 
4 

ID   ParentID 
----------- ----------- 
1   1 
2   2 
3   3 
5   5 
+1

Dies beruht auf der ersten ID 1, die nicht garantiert ist. Ein fehlgeschlagener Einfügevorgang (Timeout) könnte den ersten ID-Wert zerstören, wodurch die Tabelle permanent unterbrochen wird. – usr

+0

@usr Dies beruht auf der Identität, die mit dem Standard-Seed von 1 verwendet wird, wie in der Frage verwendet wurde. Ersetzen Sie den Samen an beiden Orten und es funktioniert immer noch. Es wird auch in der Antwort erwähnt. Ich bin jedoch ziemlich interessiert an den Details des Szenarios, wo es scheitern würde, den Tisch kaputt zu lassen. Könnten Sie bitte etwas ausarbeiten? –

+0

Fügen Sie eine Zeile in die Tabelle ein und setzen Sie die Transaktion zurück. Dies wird einen Identitätswert verschwenden. Identitätswerte werden aus Gründen der Parallelität nie zurückgesetzt. – usr

1

Wenn Sie einen expliziten Wert für die erste ID verwenden müssen, wenn Wenn Sie Ihren ersten Datensatz einfügen, können Sie die Überprüfung des IDENTITY-Werts deaktivieren (siehe: MSDN: SET IDENTITY_INSERT (Transact-SQL)).

Hier ist ein Beispiel, das dieses illistrates:

CREATE TABLE MyTable 
(
    ID int PRIMARY KEY IDENTITY(1, 1), 
    ParentID int NOT NULL, 
    CONSTRAINT MyTable_ID FOREIGN KEY (ParentID) REFERENCES MyTable(ID) 
); 

SET IDENTITY_INSERT MyTable ON; 
INSERT INTO MyTable (ID, ParentID) 
VALUES (1, 1); 
SET IDENTITY_INSERT MyTable OFF; 

WHILE @@IDENTITY <= 5 
BEGIN 
    INSERT INTO MyTable (ParentID) 
    VALUES (@@IDENTITY); 
END; 

SELECT * 
    FROM MyTable; 

IF OBJECT_ID('MyTable') IS NOT NULL 
    DROP TABLE MyTable; 
2

Es ist wie die NOT NULL Einschränkung scheint, ist für den Wurzelknoten in dem Baum nicht wahr. Es hat einfach kein Elternteil. Die Annahme, dass ParentID ist NOT NULL ist von Anfang an gebrochen.

Ich schlage vor, Sie es auf NULL festlegbare machen und einen Index für ParentID hinzufügen zu bestätigen, dass es nur ein mit Wert ist NULL:

create unique nonclustered index ... on T (ParentID) where (ParentID IS NULL) 

Es ist schwer, eine solide Baumstruktur in SQL Server zu erzwingen. Sie können zum Beispiel mehrere Wurzeln oder Zyklen in der Grafik erhalten. Es ist schwer, all das zu validieren und es ist unklar, ob es sich lohnt. Es kann gut sein, abhängig von dem speziellen Fall.

+0

Natürlich müsste man auf SQL Server 2008+ sein, um gefilterte Indizes zu verwenden ... –

+0

Interessante Lösung, aber der Kunde ist auf SQL Server 2005. – Nezreli

+0

@Nezreli Ich kann dann einen Trigger empfehlen. Ein Trigger kann eingefügte Zeilen betrachten, um praktisch alles zu überprüfen. Es könnte so aussehen: 'IF (SELECT COUNT (*) VON T WHERE ParentID ist NULL)> 1 RAISEERROR' – usr

Verwandte Themen