2010-02-05 45 views
11

Giả sử bảng với hai cột:SQL Server độc đáo auto-increment cột trong bối cảnh của một cột

ParentEntityId int foreign key 
Number int 

ParentEntityId là khóa ngoại đến bảng khác.

Number là một số địa phương, nghĩa là số duy nhất trong một đơn ParentEntityId.

Tính duy nhất có thể dễ dàng đạt được thông qua khóa duy nhất trên hai cột này.

Cách đặt Number được tự động tăng lên trong ngữ cảnh ParentEntityId khi chèn?


Phụ lục 1

Để làm rõ vấn đề, đây là một bản tóm tắt.

ParentEntity có nhiều ChildEntity, và mỗi ChiildEntity nên có một độc đáo tăng Number trong bối cảnh ParentEntity của nó.


Phụ Lục 2

Treat ParentEntity như một khách hàng.

Điều trị ChildEntity làm số Đặt hàng.

Vì vậy, các đơn đặt hàng cho mọi khách hàng phải được đánh số 1, 2, 3, v.v.

+0

Xin làm rõ 'trong bối cảnh của nó là thực thể cha mẹ' về sql ... Bối cảnh duy nhất là bảng một kỷ lục nằm trong quan hệ được thực hiện thông qua những hạn chế FK. và bạn đã có một, nếu trong thực tế 1: M là những gì bạn đang sau. Mục đích của số 'bối cảnh nhạy cảm' là gì? –

+0

@Sky: xin vui lòng, hãy xem Phụ lục 2. Hy vọng nó làm rõ ý định. –

Trả lời

9

Vâng, không có hỗ trợ cho các loại cột, nhưng bạn có thể thực hiện nó bằng cách sử dụng kích hoạt:

CREATE TRIGGER tr_MyTable_Number 
ON MyTable 
INSTEAD OF INSERT 
AS 

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 

BEGIN TRAN; 

WITH MaxNumbers_CTE AS 
(
    SELECT ParentEntityID, MAX(Number) AS Number 
    FROM MyTable 
    WHERE ParentEntityID IN (SELECT ParentEntityID FROM inserted) 
) 
INSERT MyTable (ParentEntityID, Number) 
    SELECT 
     i.ParentEntityID, 
     ROW_NUMBER() OVER 
     (
      PARTITION BY i.ParentEntityID 
      ORDER BY (SELECT 1) 
     ) + ISNULL(m.Number, 0) AS Number 
    FROM inserted i 
    LEFT JOIN MaxNumbers_CTE m 
     ON m.ParentEntityID = i.ParentEntityID 

COMMIT 

Không được thử nghiệm nhưng tôi chắc chắn nó sẽ hoạt động. Nếu bạn có khóa chính, bạn cũng có thể triển khai khóa này dưới dạng kích hoạt AFTER (Tôi không thích sử dụng các trình kích hoạt INSTEAD OF, chúng sẽ khó hiểu hơn khi bạn cần sửa đổi chúng sau 6 tháng).

Chỉ cần để giải thích những gì đang xảy ra ở đây:

  • SERIALIZABLE là chế độ cách ly nghiêm ngặt; nó đảm bảo rằng chỉ có một giao dịch cơ sở dữ liệu tại một thời điểm có thể thực hiện các câu lệnh này, mà chúng ta cần để đảm bảo tính toàn vẹn của "chuỗi" này. Lưu ý rằng điều này không thể đảo ngược thúc đẩy toàn bộ giao dịch, vì vậy bạn sẽ không muốn sử dụng điều này bên trong một giao dịch dài hạn.

  • CTE chọn số cao nhất đã được sử dụng cho từng ID mẹ;

  • ROW_NUMBER tạo chuỗi duy nhất cho từng ID mẹ (PARTITION BY) bắt đầu từ số 1; chúng tôi thêm điều này vào mức tối đa trước đó nếu có để nhận chuỗi mới.

tôi có lẽ cũng nên nhắc rằng nếu bạn chỉ bao giờ cần phải chèn một thực thể con mới tại một thời điểm, bạn nên chỉ cách chuyển những hoạt động thông qua một thủ tục lưu trữ thay vì sử dụng một kích hoạt - bạn sẽ chắc chắn có hiệu suất tốt hơn trong số đó. Đây là cách nó hiện đang được thực hiện với các cột hierarchyid trong SQL '08.

+0

@Aaronaught: vâng, tôi đang theo quy hoạch để có một khóa chính. Mã này sẽ được đơn giản hóa như thế nào trong trường hợp kích hoạt 'SAU '? Thành thật mà nói, tôi không hoàn toàn hiểu mã kích hoạt 'INSTEAD OF' của bạn. –

+0

@Anton: Tôi sẽ không nói rằng việc kích hoạt 'AFTER' đơn giản hóa, chính xác - nó chỉ thay đổi' INSERT' thành 'UPDATE' bằng' JOIN'. Nhưng các trình kích hoạt 'INSTEAD OF' có nhiều giới hạn gây phiền nhiễu khác nhau, tức là bạn chỉ có thể có một bảng cho mỗi bảng, và nó luôn có vẻ lạ khi tôi chèn lại dữ liệu' chèn'. Nhưng đó là cách nó hoạt động. Hy vọng rằng các chú thích tôi đã thêm vào giúp bạn hiểu nó tốt hơn. – Aaronaught

+0

Cảm ơn bạn, @Aaronaught. Tôi sẽ sử dụng kích hoạt như bạn đã đề xuất. Đang chấp nhận. –

0

này giải quyết được câu hỏi như tôi hiểu nó: :-)

DECLARE @foreignKey int 
SET @foreignKey = 1 -- or however you get this 

INSERT Tbl (ParentEntityId, Number) 
VALUES (@foreignKey, ISNULL((SELECT MAX(Number) FROM Tbl WHERE ParentEntityId = @foreignKey), 0) + 1) 
+0

Cách tiếp cận này có đảm bảo an toàn luồng không? Ý tôi là sẽ không xuất hiện hai bản ghi có cùng giá trị 'Số'? Có lẽ, nó nên được thực hiện trong một kích hoạt ... –

+0

@roufamatic: Anton hỏi nếu có thể tăng số ** tự động **. Việc gọi 'select MAX (...)' không giống như tạo số tự động theo cách tôi hiểu câu hỏi – Sung

+0

@Anton - chúng ta có thể bao quanh nó bằng một applock cho an toàn luồng. @Sung Meister - Tôi đồng ý không đồng ý với định nghĩa "tự động". :-) Điều này tự động cập nhật trường Số khi được gọi. – roufamatic

2

Cần thêm mệnh đề OUTPUT để kích hoạt tính không tương thích với LINQ to SQL.

Ví dụ:

INSERT MyTable (ParentEntityID, Number) 
OUTPUT inserted.* 
SELECT 
    i.ParentEntityID, 
    ROW_NUMBER() OVER 
    (
    PARTITION BY i.ParentEntityID 
    ORDER BY (SELECT 1) 
) + ISNULL(m.Number, 0) AS Number 
FROM inserted i 
LEFT JOIN MaxNumbers_CTE m 
ON m.ParentEntityID = i.ParentEntityID 
Các vấn đề liên quan