2012-11-10 36 views
7

Nếu tôi có một bảngchèn trong một tự tham chiếu bảng

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

làm thế nào để chèn đầu tiên chèo thuyền vào một bảng?

Từ quan điểm logic nghiệp vụ không hạn chế null trên ParentID không nên bỏ.

+1

Câu hỏi xen kẽ. Thậm chí bạn có thể tham khảo chính mình không? Làm thế nào bạn có thể tạo ra 1 bản ghi nếu nó mới được tạo ra? – YvesR

+2

Hàng nào là phụ huynh của hàng đầu tiên của bạn? –

+2

Đối với dòng đầu tiên ParentID phải là ID. Nếu tôi muốn sử dụng nhiều rễ trong cấu trúc cây của tôi thì bất kỳ gốc nào có ParentID == ID. Do đó, câu hỏi có thể được mở rộng như - làm thế nào tôi có thể chèn các hàng gốc?Quy trình được lưu trữ có thể sử dụng SCOPE_IDENTITY để thực hiện việc chèn này không? – Nezreli

Trả lời

5

Trong SQL Server, một INSERT đơn giản sẽ làm:

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 

kết quả trong

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

Nếu bạn đang cố gắng để chèn một giá trị đó sẽ khác nhau từ hạt giống nhận dạng của bạn, chèn sẽ thất bại.

UPDATE:

Câu hỏi đặt ra không phải là quá rõ ràng về những gì bối cảnh là (tức là mã vụ phải làm việc trong một hệ thống sản xuất trực tiếp hoặc chỉ là một thiết lập kịch bản DB) và từ các ý kiến ​​có vẻ như cứng mã hóa ID có thể không phải là một tùy chọn. Mặc dù mã ở trên thường hoạt động tốt trong các tập lệnh khởi tạo DB trong đó ID gốc phân cấp cần phải được biết và không đổi, trong trường hợp rừng (một số gốc có ID không được biết trước), các mã sau sẽ hoạt động như dự định:

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')) 

Sau đó, người dùng có thể truy vấn danh tính cuối cùng như thường lệ (SCOPE_IDENTITY, v.v.). Để mối quan tâm địa chỉ @ usr của, mã này là trong thực tế transactionally an toàn như ví dụ dưới đây trình bày:

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 

Kết quả:

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

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

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

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

Điều này phụ thuộc vào ID đầu tiên là 1 không được bảo đảm. Một chèn không (thời gian chờ) có thể phá hủy giá trị ID đầu tiên khiến bảng bị hỏng vĩnh viễn. – usr

+0

@usr Điều đó dựa vào danh tính được sử dụng với hạt giống mặc định là 1 như đã được sử dụng trong câu hỏi. Thay thế hạt giống ở cả hai nơi và nó vẫn hoạt động. Nó cũng được đề cập trong câu trả lời. Tuy nhiên, tôi khá quan tâm đến các chi tiết của kịch bản mà điều này sẽ thất bại để lại bảng bị hỏng, bạn có thể vui lòng xây dựng? –

+0

Chèn một hàng vào bảng, quay lại giao dịch. Điều này sẽ lãng phí một giá trị nhận dạng. Giá trị danh tính không bao giờ quay trở lại vì lý do đồng thời. – usr

1

Nếu bạn cần phải sử dụng một giá trị rõ ràng cho ID đầu tiên, khi bạn chèn bản ghi đầu tiên của mình, bạn có thể tắt kiểm tra giá trị IDENTITY (xem: MSDN: SET IDENTITY_INSERT (Transact-SQL)).

Dưới đây là một ví dụ mà illistrates này:

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

Nó có vẻ như các NOT NULL hạn chế là không đúng đối với nút gốc trong cây. Nó chỉ đơn giản là không có cha mẹ. Giả thiết rằng ParentIDNOT NULL bị hỏng ngay từ đầu.

Tôi đề nghị bạn thực hiện nó nullable và thêm một chỉ mục trên ParentID để xác nhận rằng chỉ có một giá trị với NULL:

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

Thật khó có thể thực thi một cấu trúc cây âm thanh trong SQL Server. Bạn có thể nhận được nhiều rễ ví dụ hoặc chu kỳ trong biểu đồ. Thật khó để xác nhận tất cả điều đó và không rõ liệu nó có đáng để nỗ lực không. Nó cũng có thể được, tùy thuộc vào trường hợp cụ thể.

+0

Tất nhiên, người ta cần phải sử dụng SQL Server 2008+ để có thể sử dụng các chỉ mục được lọc ... –

+0

Giải pháp thú vị nhưng khách hàng đang sử dụng SQL Server 2005. – Nezreli

+0

@Nezreli Tôi có thể khuyên bạn sử dụng trình kích hoạt, sau đó. Trình kích hoạt có thể xem xét các hàng được chèn để xác minh về cơ bản mọi thứ. Nó có thể trông giống như thế này: 'NẾU (CHỌN COUNT (*) TỪ TRÊN NƠI TRẺ EM IS NULL)> 1 RAISEERROR' – usr

Các vấn đề liên quan