2010-07-30 27 views
6

DBMS: MS SQL Server 2005, Standardchế độc đáo trong một nhóm các hồ sơ, nơi một số giá trị là như nhau

Tôi muốn làm một hạn chế bảng để chỉ có một kỷ lục có một giá trị đặc biệt trong vòng một tập hợp con của bảng (trong đó các hàng chia sẻ một giá trị trong một cột cụ thể). Điều này có thể không?

Ví dụ: Tôi có hồ sơ trong myTable trong đó có một chính nước ngoài không duy nhất (fk1), và một cột chút gọi là isPrimary để đánh dấu ra rằng một đặc biệt này nên được sử dụng bởi ứng dụng của chúng tôi cho logic đặc biệt.

trong trừu tượng, nó trông như thế này:

myTable 
------------- 
pk1  (int, not null) 
name  (varchar(50), null) 
fk1  (int, not null) 
isPrimary (bit, not null) 

tôi muốn đảm bảo rằng có một và chỉ một kỷ lục với cờ isPrimary thiết lập để 1, đối với mỗi giá trị duy nhất của fk1.

dữ liệu Ví dụ: này cần được pháp luật:

pk1  name  fk1 isPrimary 
---- ----- ----- ---------- 
1  Bill  111 1 
2  Tom  111 0 
3  Dick  222 1 
4  Harry 222 0 

Nhưng điều này nên không được (nhiều hơn một nơi fk = 111):

pk1  name  fk1 isPrimary 
---- ----- ----- ---------- 
1  Bill  111 1 
2  Tom  111 1 
3  Dick  222 1 
4  Harry 222 0 

Và không nên này (none trong đó fk = 222):

pk1  name  fk1 isPrimary 
---- ----- ----- ---------- 
1  Bill  111 1 
2  Tom  111 0 
3  Dick  222 0 
4  Harry 222 0 

có cách nào để làm điều này với một ràng buộc bảng?

CẬP NHẬT tôi đã đi với câu trả lời Martin Smith cho bây giờ, mặc dù tôi sẽ được thúc đẩy cấu trúc lại JohnFx trong một bản phát hành sắp tới, vì nó là giải pháp lâu dài tốt nhất. Tuy nhiên tôi muốn đăng UDF cập nhật của tôi dựa trên câu trả lời của Raze2dust, trong trường hợp người đọc trong tương lai quyết định rằng nó phù hợp hơn với nhu cầu của họ.

CREATE FUNCTION [dbo].[OneIsPrimaryPerFK1](@fk1 INT, @dummyIsPrimary BIT) 
RETURNS INT 
AS 
BEGIN 
    DECLARE @retval INT; 
    DECLARE @primarySum INT; 
    SET @retval = 0; 
    DECLARE @TempTable TABLE (
    fk1 INT, 
    PrimarySum INT) 

INSERT INTO @TempTable 
    SELECT fk1, SUM(CAST(isPrimary AS INT)) AS PrimarySum 
    FROM FacAdmin 
    WHERE fk1 = @fk1 
    GROUP BY fk1; 

    SELECT @primarySum = PrimarySum FROM @TempTable; 
    IF(@primarySum=1) 
     BEGIN 
      SET @retval = 1 
     END 
    RETURN @retval 
END; 

Thay đổi:

  1. Dùng @tempTable hơn

    temptable theo yêu cầu của udf

  2. qua @ fk1 như một tham số vì vậy tôi có thể (trong bộ nhớ v ghi vào đĩa.) chọn cho tính duy nhất trong một nhóm giá trị fk1.
  3. khó khăn cũng đã vượt qua isPrimary mặc dù nó không phải là cần thiết cho logic của chức năng, nếu không SQL2005 ưu sẽ không chạy ràng buộc kiểm tra khi isPrimary là cập nhật.

Trả lời

4

Sử dụng UDF trong các ràng buộc kiểm tra có thể không thành công theo số snapshot isolation hoặc multirow updates.

Giả sử rằng tất cả fk1 và PK1 giá trị của bạn hiện đang (và sẽ luôn luôn được) tích cực bạn có thể tạo ra một cột tính với định nghĩa sau đây

CASE WHEN isPrimary = 1 THEN fk1 ELSE -pk1 END 

sau đó thêm một hạn chế duy nhất đó. Hoặc nếu giả định rằng không thể thực hiện thì có lẽ

CASE WHEN isPrimary = 0 THEN 1.0/pk1 ELSE fk1 END 
+0

Mặc dù giải pháp của JohnFx là giải pháp lâu dài tốt nhất, đây là giải pháp cho tôi ngay bây giờ. –

6

SQL 2005 không cung cấp khả năng áp dụng mệnh đề Where cho một chỉ mục duy nhất như SQL 2008.Tuy nhiên, có một số cách để giải quyết vấn đề trong SQL 2005:

  1. Tạo chế độ xem được lập chỉ mục lọc IsPrimary = 1 và thêm chỉ mục duy nhất cho chế độ xem đó.
  2. Tạo trình kích hoạt nơi bạn đảm bảo chỉ một trình kích hoạt có thể là chính.
  3. Đóng gói logic của bạn thành một proc được lưu trữ và buộc người dùng phải đi qua proc được lưu trữ để chèn hoặc cập nhật từ bảng của bạn.
+2

4. Tạo một chức năng lưu trữ mà bạn sử dụng trong một hạn chế kiểm tra – Frank

+1

Cảnh báo: Một udf trong một hạn chế có thể làm việc, nhưng nó cũng có thể gây ra vấn đề hiệu suất nghiêm trọng nếu nó đang làm lựa chọn. – JohnFx

+1

Hình phạt hiệu suất sẽ chỉ được chèn và cập nhật, đúng không? Tôi không thể tưởng tượng một ràng buộc kiểm tra kích hoạt khi chỉ chọn dữ liệu. Nếu đó là sự thật, tôi không sao lấy được hit, vì bàn không được viết rất thường xuyên. –

3

Bạn có thể thử tạo ra một chức năng và sau đó sử dụng một hạn chế kiểm tra:

CREATE FUNCTION ChkFn() 
RETURNS INT 
AS 
BEGIN 
    DECLARE @retval INT 
    DECLARE @distinct INT 
    DECLARE @top INT 
    SET @retval = 0 
    SELECT fk1 AS ForeignKey, SUM(isPrimary) AS PrimarySum 
    INTO #TempTable 
    FROM myTable 
    GROUP BY fk1 
    SELECT @distinct = COUNT(DISTINCT(PrimarySum)) FROM #TempTable 
    SELECT @top = top PrimarySum FROM #TempTable 
    IF(@distinct=1 AND @top=1) 
    BEGIN 
    @retval = 1 
    END 
    RETURN @retval 
END; 
GO 

ALTER TABLE myTable 
ADD CONSTRAINT chkFkPk CHECK (dbo.ChekFn() = 1); 
GO 

Hãy thử nó và cho tôi biết nếu nó làm việc. Không phải là rất thanh lịch mặc dù ..

+0

Điều này rất gần và giúp tôi đi đúng hướng, vì vậy tôi sẽ đánh dấu nó là được chấp nhận. Tôi sẽ đăng chức năng đã hoàn thành vào thứ Hai. Vấn đề đầu tiên là bạn không thể sử dụng #TempTables trong UDF, bạn phải sử dụng @TempTable. Của người khác tôi sẽ giải quyết trong bài viết cập nhật. –

+1

Có thể thất bại đối với các bản cập nhật đa cấp hoặc dưới sự cô lập ảnh chụp nhanh. –

+0

ah yea quên về UDF ... Không có ý tưởng về cập nhật multirow/snapshot cô lập. Thx Martin cho bài viết .. Tôi sẽ cố gắng hiểu nó. Tôi là một newbie :-) –

7

Bắt đầu một câu trả lời mới kể từ khi tôi đọc sai câu hỏi đầu tiên.

Có vẻ như bạn có thể giải quyết vấn đề bằng cách xem xét lại thiết kế bảng của bạn một chút để tránh làm cho bạn khó khăn trong việc thực thi quy tắc kinh doanh của mình.

Làm thế nào về việc bỏ cột IsPrimary khỏi MyTable và thêm cột PrimaryPersonID vào bảng khác tham chiếu đến người chính?

Bằng cách đó, cấu trúc sẽ thực thi rằng 1 và chỉ 1 mục nhập trong bảng FK là chính cho mỗi người.

+2

Bạn hoàn toàn đúng, đây phải là mô hình dữ liệu và tôi muốn chúng tôi có thể thực hiện điều này. Hiện tại, chúng tôi có một vài ứng dụng khác nhau chỉ vào điều này và chúng tôi không thể thay đổi tất cả các ứng dụng này để truy cập vào mô hình được cập nhật ngay bây giờ. Tôi sẽ đẩy để thực hiện thay đổi này trong tương lai. –

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