Các ràng buộc dựa trên các cột khác


7

Có thể giới hạn những giá trị nào được cho phép trong một cột dựa trên các giá trị khác trong hàng không?

Ví dụ: bảng của tôi:

ID  Test_mode  Active
--  ---------  ------
1   1          Null
2   0          1
3   1          0

Có cách nào để một trong hai thay đổi giá trị của Test_modeđể 0nếu một 1được đưa vàoActive

HOẶC LÀ

Nếu Test_modelà 1 không cho phép chèn / cập nhậtActive

HOẶC LÀ

Ném một số loại lỗi nếu Test_modelà 1 và Activecố gắng chèn / cập nhật .

Activechỉ có thể là NULL, 1, 0 và AND chỉ 1 với Test_mode0.

Tôi hy vọng điều này có ý nghĩa, nếu không cho tôi biết và tôi sẽ cập nhật câu hỏi.


Tôi chỉ muốn giải thích tại sao tôi chấp nhận câu trả lời tôi đã làm. Câu trả lời của Jon và Kin đều rất hữu ích và nhiều thông tin. Kins trả lời các liên kết đến một trang khác có lời giải thích rất tốt về các ràng buộc.
Archangel33

Câu trả lời:


8

Trước hết, Chào mừng bạn đến với dba.stackexchange.com và cảm ơn bài viết của bạn !!

Có thể giới hạn những giá trị nào được cho phép trong một cột dựa trên các giá trị khác trong hàng.

Có sử dụng KIỂM TRA CONSTRAINTS như được mô tả ở đây

Thí dụ :

create table myTable (ID int identity(1,1)
                        , Test_mode int
                        , Active int 
                        )
go

-- Active can only be NULL, 1, 0, AND only 1 with Test_mode as 0.
ALTER TABLE myTable WITH CHECK ADD 
   CONSTRAINT ck_active CHECK (active IS NULL OR active IN (1, 0)) 
   go

-- some test data
insert into myTable (test_mode, Active) values (1, null)
insert into myTable (test_mode, Active) values (0, null)
insert into myTable (test_mode, Active) values (1, 0)
insert into myTable (test_mode, Active) values (0, 1)
insert into myTable (test_mode, Active) values (1, 1)

select * from myTable

-- Is there a way to either change the value of Test_mode to 0 if a 1 is inserted into Active

update myTable
set Test_mode = case when Active = 1 then  0
        else Test_mode 
        end
where Active = 1

Nếu Test_modelà 1 không cho phép chèn / cập nhật Active --OR-- Ném một số loại lỗi nếu Test_mode là 1 và cố gắng chèn / cập nhật Active.

Sử dụng TRY / CATCH như được mô tả ở đây


Cảm ơn bạn đã trả lời của bạn. Tôi không có ý tưởng về Kiểm tra ràng buộc. các liên kết là rất hữu ích. Mặc dù vậy, bản cập nhật sẽ phải được xác định rõ ràng mỗi khi tôi muốn chèn một giá trị? Tôi cần thiết lập tính năng này để bất cứ khi nào một trong số các nhà phát triển của chúng tôi viết Quy trình được lưu trữ mà họ sẽ không (Mặc dù họ nên!) Để biết rằng đây là cách để cập nhật bảng. Họ chỉ cần có thể Update myTable Set Active = 1test_modeđược cập nhật là tốt. Nếu đây là cách duy nhất, vậy thì hãy là nó! Cảm ơn bạn :)
Archangel33

@ Archangel33 Có .. đó là đặt cược tốt nhất của bạn để thực hiện cập nhật. Bạn có thể gói nó trong SP, vì vậy bạn không cần phải làm điều đó mọi lúc. Đồng thời kiểm tra phương pháp TRY / CATCH để tạo lỗi và báo cáo lại cho người dùng. Ngoài ra, nếu bạn cảm thấy rằng tôi đã trả lời câu hỏi của bạn, xin vui lòng đánh dấu nó là một câu trả lời.
Kin Shah

1
Active int [...] CHECK (active IS NULL OR active IN (1, 0))- điều này có vẻ như là thiết kế tồi vô nghĩa, vì nó có nghĩa là 'vui lòng mô phỏng một boolean bit, nhưng lưu trữ nó trong một loại kém hiệu quả hơn và yêu cầu tôi phải nhớ rằng tôi phải tự giới hạn các giá trị được phép của nó`
underscore_d

3

Dòng phòng thủ đầu tiên để bảo vệ chống lại dữ liệu không hợp lệ xâm nhập vào các bảng của bạn là các loại dữ liệu của các cột.

Nếu một quá trình cố gắng chèn hoặc cập nhật một cột thành một giá trị nằm ngoài phạm vi của kiểu dữ liệu (hoặc NULLnếu cột không cho phép NULLs), thao tác sẽ thất bại ngay lập tức mà bạn không cần phải thực hiện thêm bất kỳ công việc nào.

Lựa chọn kiểu dữ liệu là một trong những khía cạnh quan trọng nhất của thiết kế bảng.

Vì vậy, vì bạn đã không đăng một lược đồ, tôi sẽ xây dựng một lược đồ dựa trên thông tin bạn đã cung cấp:

CREATE TABLE [dbo].[Tests]
(
    ID int IDENTITY(1, 1) PRIMARY KEY,
    Test_mode bit NOT NULL, /* Based on only seeing 0/1. Maybe tinyint? */
    Active bit NULL
);

Dựa trên thiết kế này, các kết hợp có sẵn đã bị giới hạn ở những điều sau đây:

Test_mode hoạt động
        0 NULL
        0 0
        0 1
        1 NULL
        1 0
        1 1

Bất cứ điều gì khác hơn sẽ gây ra lỗi được nêu ra. (Đó là một điều tốt.)


Có cách nào để thay đổi giá trị của Test_mode thành 0 nếu 1 được chèn vào Active

HOẶC LÀ

Nếu Test_mode là 1 thì không cho phép chèn / cập nhật Active

HOẶC LÀ

Ném một số loại lỗi nếu Test_mode là 1 và cố gắng chèn / cập nhật Active.

Hoạt động chỉ có thể là NULL, 1, 0 và AND chỉ 1 với Test_mode là 0.

Bạn đã đưa ra 4 cách khác nhau để đi đến sự kết hợp các giá trị được phép (tốt, sắp xếp). Đây là những chiến lược rất khác nhau , với những hành vi thực hiện rất khác nhau.

Tôi thích sử dụng những gì được gọi là ràng buộc khai báo . Nói cách khác, lược đồ bảng và các đối tượng liên quan của nó giới hạn các giá trị được phép bằng cách khai báo rõ ràng những gì được phép (hoặc đôi khi, những gì không được phép). Trong thực tế, chính các kiểu dữ liệu cột là một loại ràng buộc khai báo. Càng gần với dữ liệu bảng, các giá trị có thể bị hạn chế, chúng càng dễ bị hạn chế và đáng tin cậy hơn. (Ngược lại, một ràng buộc không khai báo hoặc hoạt động sẽ được thực hiện bằng cách viết một đoạn T-SQL, thường là một trình kích hoạt bảng hoặc một phần của thủ tục được lưu trữ.)

3 tùy chọn đầu tiên chỉ có thể được thực hiện bằng các phương tiện không khai báo. Tuy nhiên, cái cuối cùng là khai báo, vì vậy hãy tập trung vào đó:

Activechỉ có thể là NULL, 1, 0 và AND chỉ 1 với Test_mode0.

Điều này xác định những gì bạn thực sự muốn, đó là sự kết hợp các giá trị được phép trong bảng. Lưu ý rằng các kết hợp hợp lệ chỉ phụ thuộc vào các giá trị cột trong cùng một hàng . Điều này rất quan trọng, vì nó xác định (các) cơ chế nào có thể được sử dụng để thực hiện các ràng buộc.

Trong trường hợp này, chúng ta có thể sử dụng một CHECKhạn chế , đó là một / kiểm tra đúng sai quyết định xem một hàng là hợp lý hay không dựa trên một của hàng giá trị cột 1 . Nếu thử nghiệm thất bại, hoạt động cố gắng thay đổi hàng sẽ thất bại với một lỗi.

ALTER TABLE [dbo].[Tests] WITH CHECK
    ADD CONSTRAINT CC_Tests_TestMode_Active
        CHECK ((Test_mode != 0) OR ((Active IS NOT NULL) AND (Active = 1)));

Bạn sẽ lưu ý rằng tôi đã xây dựng vị từ sao cho nó sẽ tiếp tục hoạt động ngay cả khi Test_modethực sự là một kiểu số nguyên (không thể rỗng). Phần IS NOT NULLnày là bắt buộc vì các CHECKràng buộc cho phép các hàng trong đó vị từ ước tính undefined.

1 Chúng có thể được sử dụng để kiểm tra bên ngoài hàng hiện tại, nhưng đây là một thực tiễn tồi và tôi sẽ không tham gia vào đó. Sử dụng một kích hoạt thay thế.


Xin lỗi về lược đồ tôi sẽ nhớ để đăng những câu hỏi trong tương lai. Sử dụng Check Constraintcó vẻ như là giải pháp thích hợp nhất. Được kiểm tra các ràng buộc sau một tuyên bố cập nhật? Ý tôi là, Sẽ UPDATEgiống như UPDATE [dbo].[Tests] SET Active=1,test_mode=0 WHERE test_mode=1thất bại một ràng buộc hoặc là trạng thái của hàng chỉ được kiểm tra sau khi UPDATEhoàn thành. Tôi sẽ làm một số thử nghiệm về điều này để đảm bảo nhưng tôi có cảm giác rằng việc này sẽ hiệu quả.
Archangel33

@ Archangel33: Kiểm tra các ràng buộc chỉ xác nhận trạng thái cuối cùng của hàng. Họ không xác thực liệu bạn có được phép thay đổi từ trạng thái này sang trạng thái khác hay không. Các UPDATEtuyên bố bạn đề cập đến sẽ thành công, như trạng thái cuối cùng được cho phép. Nếu bạn muốn không cho phép thay đổi trạng thái đó, bạn phải sử dụng một kích hoạt để làm điều đó. (Các yêu cầu ban đầu của bạn có phần không rõ ràng.) Lưu ý: Tôi sẽ khuyên bạn nên sử dụng cả ràng buộc kiểm tra kích hoạt nếu đó là những yêu cầu của bạn.
Jon Seigel

Cảm ơn bạn. Có tôi sẽ xác định yêu cầu của tôi tốt hơn vào lần tới. Không, nó hoàn hảo Tôi rất vui khi biết rằng nó UPDATEsẽ hoạt động. Tôi đã nghĩ rằng nó có thể gây ra trạng thái không hợp lệ trong khi chuyển từ trạng thái hiện tại sang trạng thái mong muốn.
Archangel33

2
CREATE UNIQUE INDEX [UNQ_IndexName]
  ON [dbo].[Table]([Column])
  WHERE   ([Status] in( 'A','D' ) );

1
Mặc dù điều này không khớp với ví dụ được trình bày bởi OP, nhưng nó thực sự đại diện cho một ví dụ về các giá trị trong một cột giới hạn các giá trị trong một cột khác. Nếu Statuslà bất cứ điều gì nhưng Ahoặc D, thì Columnsẽ không phải là duy nhất.
RDFozz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.