2012-02-14 30 views
10

schema SQL của tôi làDừng MySQL chịu đựng nhiều NULLs trong một hạn chế UNIQUE

CREATE TABLE Foo (
`bar` INT NULL , 
`name` VARCHAR (59) NOT NULL , 
UNIQUE (`name`, `bar`) 
) ENGINE = INNODB; 

MySQL được phép tuyên bố sau được lặp đi lặp lại, kết quả là bản sao.

INSERT INTO Foo (`bar`, `name`) VALUES (NULL, 'abc'); 

mặc dù có

UNIQUE (`name`, `bar`) 

Tại sao điều này dung nạp và làm thế nào để ngăn chặn nó?

+2

Làm cho nó một tiểu , không cho phép null trong cả hai trường. Hoặc làm cho các trường không rỗng. Theo tài liệu mysql: http://dev.mysql.com/doc/refman/5.1/en/create-index.html "Đối với tất cả các công cụ, một chỉ số UNIQUE cho phép nhiều giá trị null cho các cột có thể chứa null". –

+0

'NULL' không phải là giá trị. Như @MarcB nói, bạn cần phải không cho phép 'NULL' cho cái này – JNK

+0

Có vẻ như đó là một lỗi: http://bugs.mysql.com/bug.php?id=8173. (Ít nhất một số người trong báo cáo lỗi nghĩ như vậy) –

Trả lời

12

Cảnh báo: Câu trả lời này đã lỗi thời. Kể từ MySQL 5.1, BDB không được hỗ trợ.

Tùy thuộc vào MySQL Engine Type. BDB không cho phép nhiều giá trị NULL sử dụng UNIQUE nhưng MyISAMInnoDB cho phép nhiều NULL s ngay cả với UNIQUE.

2

Nói chung, tùy thuộc vào công cụ lưu trữ, NULL có thể hoặc không được xem là giá trị duy nhất. Bạn phải sử dụng công cụ lưu trữ không nhận dạng NULL làm giá trị duy nhất, ví dụ: InnoDB hoặc MyISAM.

Để tránh điều này, bạn có thể tạo "giá trị rỗng", chẳng hạn như 99999999, bạn có thể nhận dạng là NULL vì không có cách nào thay đổi cách công cụ lưu trữ của bạn quyết định xử lý null trong các khóa duy nhất.

+4

-1 để đề xuất giải pháp số ma thuật. Câu trả lời thực sự là hiểu 'NULL' và sử dụng nó một cách thích hợp. – JNK

+0

Tôi sẽ không đưa ra -1 vì OP dường như nghĩ rằng 'NULL' thực sự là một số giá trị kỳ diệu. Cũng không phải +1 vì không hiểu rõ khái niệm về khái niệm. –

0

BDB không cho phép nhiều giá trị NULL sử dụng UNIQUE. Nhưng MySQL thả động cơ BDB (http://dev.mysql.com/doc/relnotes/mysql/5.1/en/news-5-1-12.html).

Vì vậy, bây giờ: http://dev.mysql.com/doc/refman/5.5/en/create-index.html

Đối với tất cả động cơ, một chỉ số UNIQUE phép nhiều giá trị NULL cho các cột có thể chứa NULL. Nếu bạn chỉ định giá trị tiền tố cho một cột trong chỉ mục UNIQUE, các giá trị cột phải là duy nhất trong tiền tố.

1

CẬP NHẬT: Bạn nên sử dụng ý tưởng được gợi ý bởi @greenoldman trong nhận xét bên dưới để thay thế. Tạo một trường boolean với các trigger để thiết lập giá trị dựa trên trường nullable của bạn là NULL hay không và sau đó kết hợp trường boolean trong một ràng buộc duy nhất với các trường khác xác định tính duy nhất.


Tôi đã tìm ra cách giải quyết vấn đề này nếu bạn phải thực thi ràng buộc duy nhất nhưng cũng cần phải có khóa ngoài trên cột, do đó yêu cầu nó phải rỗng. Giải pháp của tôi có nguồn gốc từ this và sẽ cần thêm một chút không gian. Đây là một ví dụ với trường id số.

Khái niệm cơ bản là bạn phải tạo một trường không nullable khác sẽ có giá trị của trường rỗng của bạn với khóa ngoài được sao chép vào nó bằng trình kích hoạt. Ràng buộc duy nhất sau đó sẽ được thực thi trên trường trùng lặp không thể vô hiệu.Để làm điều này bạn cần phải xác định một lĩnh vực không nullable với một giá trị mặc định của 0 tương tự như sau:

ALTER TABLE `my_table` ADD `uniq_foo` int(10) UNSIGNED NOT NULL DEFAULT '0'; 

Sau đó, bạn chỉ cần xác định một số trigger như thế này:

DROP TRIGGER IF EXISTS `my_table_before_insert`; 
DELIMITER ;; 
CREATE TRIGGER `my_table_before_insert` BEFORE INSERT ON `my_table` 
FOR EACH ROW 
BEGIN 
    SET NEW.uniq_foo = IFNULL(NEW.foo_id, 0); 
END;; 
DELIMITER ; 

DROP TRIGGER IF EXISTS `my_table_before_update`; 
DELIMITER ;; 
CREATE TRIGGER `my_table_before_update` BEFORE UPDATE ON `my_table` 
FOR EACH ROW 
BEGIN 
    SET NEW.uniq_foo = IFNULL(NEW.foo_id, 0); 
END;; 
DELIMITER ; 
+0

Cảm ơn bạn rất nhiều! Khái niệm này rất tuyệt vời, nhưng việc thực hiện quá mong manh để sử dụng. Tuy nhiên có một biện pháp khắc phục - cột bổ sung này phải là 'TINYINT (1)' (có nghĩa là - bool) giữ '0' cho null và' 1' cho không null (hoặc ngược lại, nó không quan trọng) cho trường nullable. Chỉ mục duy nhất nên sử dụng cột bổ sung này ** thay vào đó ** chứ không phải thay thế. Và đó là nó - không có cơ hội bạn sẽ nhận được xung đột vì giá trị ma thuật, bởi vì không có. – greenoldman

+0

@greenoldman Ý tưởng tuyệt vời! FWIW, tôi đã không gặp rắc rối khi sử dụng kỹ thuật này trong sản xuất với các bộ dữ liệu rất lớn và hoạt động nhưng tôi vẫn thích ý tưởng của bạn tốt hơn. –

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