2012-11-10 17 views
7

Jeśli mam tabeliwstawiania na własny odwołuje tabeli

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

jaki sposób można wstawić pierwszy wiersz do tabeli?

Z punktu widzenia logiki biznesowej, nie ograniczenie null na ParentID nie powinno zostać usunięte.

+1

Pytanie interstingowe. Czy możliwe jest nawet odniesienie do siebie? Jak możesz utworzyć 1 rekord, jeśli jest on nowy? – YvesR

+2

Który wiersz jest rodzicem pierwszego rzędu? –

+2

Dla pierwszego wiersza ParentID powinien być identyfikatorem. Jeśli chcę użyć wielu korzeni w strukturze drzewa, to każdy root ma ParentID == ID. Dlatego pytanie może zostać rozszerzone jako - jak mogę wstawić wiersze główne?Czy procedura składowana może użyć SCOPE_IDENTITY, aby wstawić tę wstawkę? – Nezreli

Odpowiedz

5

W SQL Server, prosty INSERT zrobi:

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 

skutkuje

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

Jeśli próbujesz wstawić wartość, która będzie inna od swojej tożsamości nasion, wstawienie zawiedzie.

UPDATE:

Pytanie nie jest zbyt jasne, na co kontekst jest (czyli to kod powinien działać w żywych systemu produkcyjnego lub po prostu skrypt instalacyjny DB) oraz z uwag wydaje się trudne kodowania identyfikator może nie być opcją. Chociaż powyższy kod powinien normalnie działać poprawnie w skryptach inicjalizujących bazy danych, w których hierarchiczny identyfikator główny może być znany i stały, w przypadku lasu (kilka katalogów z identyfikatorami nieznanymi wcześniej) następujące czynności powinny działać zgodnie z przeznaczeniem:

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

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

Następnie można było zapytać o ostatnią tożsamość jak zwykle (SCOPE_IDENTITY itd.). Obaw adres @ usr jest, kod jest w rzeczywistości transakcyjnie bezpieczne, jak w poniższym przykładzie pokazuje:

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 

Rezultat:

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

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

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

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

To zależy od pierwszego identyfikatora 1, który nie jest gwarantowany. Wadliwa wstawka (timeout) może zniszczyć pierwszą wartość identyfikatora, powodując trwałe uszkodzenie tabeli. – usr

+0

@usr To zależy od tożsamości używanej z domyślnym początkiem 1, jak użyto w pytaniu. Wymień nasiono w obu miejscach i nadal działa. Jest to również wspomniane w odpowiedzi. Jestem jednak bardzo zainteresowany szczegółami scenariusza, w których to nie powiedzie się, jeśli zostanie złamany stół, czy mógłbyś go rozwinąć? –

+0

Wstaw wiersz do tabeli, wycofaj transakcję. To zmarnuje jedną wartość tożsamości. Wartości tożsamości nigdy nie są wycofywane ze względu na współbieżność. – usr

1

Jeśli trzeba użyć wyraźną wartość dla pierwszego identyfikatora, gdy wstawiasz swój pierwszy rekord, możesz wyłączyć sprawdzanie wartości IDENTYFIKACJI (patrz: MSDN: SET IDENTITY_INSERT (Transact-SQL)).

Oto przykład, że illistrates to:

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

Wydaje się, że NOT NULL przymusu nie jest prawdziwe dla węzła głównego w drzewie. Po prostu nie ma rodzica. Zatem założenie, że ParentID jest jest zerwane od początku.

Proponuję zrobić to pustych i dodać indeks na ParentID aby potwierdzić, że istnieje tylko jedna z wartości NULL:

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

Trudno egzekwować strukturę drzewa dźwięk w SQL Server. Możesz uzyskać wiele korzeni na przykład lub cykli na wykresie. Trudno to potwierdzić i nie jest jasne, czy jest to warte wysiłku. Może tak być, w zależności od konkretnego przypadku.

+0

Oczywiście trzeba być na SQL Server 2008+, aby móc korzystać z filtrowanych indeksów ... –

+0

Ciekawe rozwiązanie, ale klient jest na serwerze SQL Server 2005. – Nezreli

+0

@Nezreli Mogę polecić za pomocą wyzwalacza, a następnie. Wyzwalacz może przeglądać wstawione wiersze, aby zweryfikować praktycznie wszystko. Mogłoby to wyglądać tak: 'JEŻELI (WYBIERZ LICZBĘ (*) Z T, GDZIE identyfikator rodzica jest NULL)> 1 RAISEERROR' – usr

Powiązane problemy