Ограничение CHECK в MySQL не работает

126

Сначала я создал таблицу вроде

CREATE TABLE Customer (
  SD integer CHECK (SD > 0),
  Last_Name varchar (30),
  First_Name varchar(30)
);

а затем вставил значения в эту таблицу

INSERT INTO Customer values ('-2','abc','zz');

MySQL не показывает ошибки, он принял значения.

JohnRaja
источник
Частично согласен. Учитывая, что вы пытались его использовать, можно предположить, что вы задавали оба вопроса. Фактически, принятый вами ответ в основном объясняет, почему он не работает.
igorrs
1
Вы можете проголосовать за этот запрос функции: bugs.mysql.com/bug.php?id=3464, но он не привлекал внимания уже десять лет.
Джаред Бек,
11
Вы можете использовать ограничения CHECK в MariaDB, начиная с версии 10.2.1 .
Joanq

Ответы:

140

MySQL 8.0.16 - первая версия, которая поддерживает ограничения CHECK.

Прочтите https://dev.mysql.com/doc/refman/8.0/en/create-table-check-constraints.html

Если вы используете MySQL 8.0.15 или более раннюю версию, в Справочном руководстве MySQL говорится:

Предложение CHECKанализируется, но игнорируется всеми механизмами хранения.

Попробуйте триггер ...

mysql> delimiter //
mysql> CREATE TRIGGER trig_sd_check BEFORE INSERT ON Customer 
    -> FOR EACH ROW 
    -> BEGIN 
    -> IF NEW.SD<0 THEN 
    -> SET NEW.SD=0; 
    -> END IF; 
    -> END
    -> //
mysql> delimiter ;

Надеюсь, это поможет.

Дэвид Керинс
источник
9
Здесь вы узнаете, как вместо этого вызвать ошибку: stackoverflow.com/a/7189396/1144966
petermeissner
41
Это одна из множества ярких причин, по которым я всегда буду использовать PostgreSQL вместо MySQL, имея какой-либо выбор.
Reinderien
5
Интересно, будет ли разработка в MySQL 10 или 15 минут, чтобы выдавать предупреждение, если парсер обнаруживает определенное CHECKограничение. Ааа, это было бы слишком просто ...
Габорш
75

К сожалению, MySQL не поддерживает проверочные ограничения SQL. Вы можете определить их в своем DDL-запросе по соображениям совместимости, но они просто игнорируются.

Есть простая альтернатива

Вы можете создать BEFORE INSERTи BEFORE UPDATEтриггера , которые либо вызывают ошибку или установить поле значения по умолчанию , когда требование данных не выполняется.

Пример BEFORE INSERTработы после MySQL 5.5

DELIMITER $$
CREATE TRIGGER `test_before_insert` BEFORE INSERT ON `Test`
FOR EACH ROW
BEGIN
    IF CHAR_LENGTH( NEW.ID ) < 4 THEN
        SIGNAL SQLSTATE '12345'
            SET MESSAGE_TEXT := 'check constraint on Test.ID failed';
    END IF;
END$$   
DELIMITER ;  

До MySQL 5.5 вы должны были вызвать ошибку, например вызвать неопределенную процедуру.

В обоих случаях это вызывает неявный откат транзакции. MySQL не разрешает использование самого оператора ROLLBACK в процедурах и триггерах.

Если вы не хотите откатывать транзакцию (INSERT / UPDATE должен пройти даже при неудачном "ограничении проверки", вы можете перезаписать значение, используя SET NEW.ID = NULLкоторое установит идентификатор в значение по умолчанию для полей, на самом деле не имеет смысла для идентификатора Тхо

Изменить: удалена случайная цитата.

По поводу :=оператора:

В отличие от =этого :=оператор никогда не интерпретируется как оператор сравнения. Это означает, что вы можете использовать :=любой допустимый оператор SQL (не только в операторах SET) для присвоения значения переменной.

https://dev.mysql.com/doc/refman/5.6/en/assignment-operators.html

Относительно кавычек идентификаторов обратных ссылок:

Символ кавычки идентификатора - это обратная кавычка («»).

Если включен режим ANSI_QUOTES SQL, также допустимо заключать идентификаторы в двойные кавычки.

http://dev.mysql.com/doc/refman/5.6/en/identifiers.html

Мишель Фельдхейм
источник
7
... не очень просто, по крайней мере, по сравнению с CHECK :(. Coupla tutes: net.tutsplus.com/tutorials/databases/… , sitepoint.com/how-to-create-mysql-triggers
Бен,
тьфу, это выглядит очень громоздко. Думаю, я лучше создам кортеж в python и проверю значения там, чем
вставляю
Быстрый вопрос: почему это не работает без установки DELIMITER?
ddz
52

CHECK MySQL игнорирует ограничения, как объясняется в небольшом комментарии в документации: CREATE TABLE

Предложение CHECKанализируется, но игнорируется всеми механизмами хранения.

ypercubeᵀᴹ
источник
2
@thefiloe: Правильно, в других СУБД с правильной реализацией CHECKограничений, если CHECKоценивается как, FALSEто вставка (или обновление) не выполняется и возникает ошибка.
ypercubeᵀᴹ 09
Исправлено в MariaDB (см. Этот ответ stackoverflow.com/a/44333349 ).
Жером
@ Jérôme Я знаю, у меня есть несколько (более свежих) ответов, которые включают улучшения в этой области (были другие способы решения этой проблемы, как в MariaDB, так и в MySQL, до того, как MariaDB правильно реализовала ограничения CHECK). Я не уверен, стоит ли мне пойти и отредактировать все свои старые ответы!
ypercubeᵀᴹ 02
Я полагаю, что мой комментарий со ссылкой на более свежий ответ в порядке. Или лучше, чем ничего. Возможно, мне следовало отредактировать. Я не хотел заставлять вас что-либо делать.
Жером
1

Проверочные ограничения поддерживаются начиная с версии 8.0.15 (еще не выпущенной)

https://bugs.mysql.com/bug.php?id=3464

[23 янв, 16:24] Поль Дюбуа

Сообщение разработчика: Исправлено в 8.0.15.

Ранее MySQL допускал ограниченную форму синтаксиса ограничения CHECK, но анализировал и игнорировал его. MySQL теперь реализует основные функции ограничений CHECK таблиц и столбцов для всех механизмов хранения. Ограничения определяются с помощью операторов CREATE TABLE и ALTER TABLE.

Джеймс
источник
1

Обновите MySQL до версии 8.0.16, чтобы использовать checks:

Начиная с MySQL 8.0.16, CREATE TABLE разрешает основные функции ограничений CHECK таблиц и столбцов для всех механизмов хранения. CREATE TABLE разрешает следующий синтаксис ограничения CHECK как для ограничений таблицы, так и для ограничений столбца

Документация по проверкам MySQL

sdlins
источник
-2

попробуйте с set sql_mode = 'STRICT_TRANS_TABLES'ИЛИSET sql_mode='STRICT_ALL_TABLES'

Kanagu
источник
2
этот actall не помогает (MySQL 5.6), он предотвращает ввод данных ложного типа, но не CHECK
вводит