La restricción CHECK en MySQL no funciona

126

Primero creé una tabla como

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

y luego insertó valores en esa tabla

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

MySQL no muestra un error, aceptó los valores.

JohnRaja
fuente
Parcialmente de acuerdo. Dado que trató de usarlo, se puede suponer que estaba haciendo ambas preguntas. De hecho, la respuesta que ha aceptado explica principalmente por qué no funciona.
igorrs
1
Puede votar sobre esta solicitud de función: bugs.mysql.com/bug.php?id=3464 pero no ha recibido ninguna atención en una década.
Jared Beck
11
Puede usar las restricciones CHECK en MariaDB desde la versión 10.2.1 .
joanq

Respuestas:

140

MySQL 8.0.16 es la primera versión que admite restricciones CHECK.

Lea https://dev.mysql.com/doc/refman/8.0/en/create-table-check-constraints.html

Si usa MySQL 8.0.15 o anterior, el Manual de referencia de MySQL dice:

La CHECKcláusula es analizada pero ignorada por todos los motores de almacenamiento.

Prueba un disparador ...

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 ;

Espero que ayude.

David Kerins
fuente
9
Aquí encontrará cómo activar un error: stackoverflow.com/a/7189396/1144966
petermeissner
41
Esta es una de las muchas razones por las que siempre usaré PostgreSQL en lugar de MySQL si tengo alguna opción.
Reinderien
55
Me pregunto si sería un desarrollo de 10 minutos o 15 minutos en MySQL lanzar una advertencia si el analizador encuentra una CHECKrestricción definida. Ahhh, eso sería demasiado sencillo ...
gaborsch
75

Desafortunadamente, MySQL no admite restricciones de verificación de SQL. Puede definirlos en su consulta DDL por razones de compatibilidad, pero simplemente se ignoran.

Hay una alternativa simple

Puedes crear BEFORE INSERTyBEFORE UPDATE disparadores que causen un error o establecer el campo en su valor predeterminado cuando no se cumplen los requisitos de los datos.

Ejemplo para BEFORE INSERTtrabajar después de 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 ;  

Antes de MySQL 5.5 tenía que causar un error, por ejemplo, llamar a un procedimiento indefinido.

En ambos casos, esto provoca una reversión de transacción implícita. MySQL no permite la declaración ROLLBACK en sí misma dentro de procedimientos y disparadores.

Si no desea revertir la transacción (INSERT / UPDATE debe pasar incluso con una "restricción de verificación" fallida, puede sobrescribir el valor usando el SET NEW.ID = NULLcual establecerá la identificación en el valor predeterminado de los campos, realmente no tiene sentido para una identificación aunque

Editar: se eliminó la cita perdida.

Sobre el :=operador:

A diferencia =, el :=operador nunca se interpreta como un operador de comparación. Esto significa que puede usar :=cualquier instrucción SQL válida (no solo en las instrucciones SET) para asignar un valor a una variable.

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

En cuanto a las comillas del identificador de retroceso:

El carácter de comilla del identificador es la tecla de retroceso ("" ")

Si el modo ANSI_QUOTES SQL está habilitado, también está permitido citar identificadores dentro de comillas dobles

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

Michel Feldheim
fuente
77
... no es muy simple, al menos en comparación con CHECK :(. Coupla tutes: net.tutsplus.com/tutorials/databases/… , sitepoint.com/how-to-create-mysql-triggers
Ben
ugh esto se ve muy voluminoso. Creo que prefiero crear una tupla en Python y verificar los valores allí en lugar de poner esto.
OzzyTheGiant
Pregunta rápida: ¿por qué esto no funciona sin configurar el DELIMITER?
ddz
52

CHECK MySQL ignora las restricciones como se explica en un comentario minúsculo en los documentos: CREATE TABLE

La CHECKcláusula es analizada pero ignorada por todos los motores de almacenamiento.

ypercubeᵀᴹ
fuente
2
@thefiloe: Correcto, en otros DBMS con implementación correcta de CHECKrestricciones, si se CHECKevalúa a FALSEcontinuación, la inserción (o actualización) no se realiza y se produce un error.
ypercubeᵀᴹ
Solucionado en MariaDB (consulte esta respuesta stackoverflow.com/a/44333349 ).
Jérôme
@ Jérôme Lo sé, tengo algunas respuestas (más recientes) que incluyen mejoras en esta área (hubo otras formas de solucionar este problema, tanto en MariaDB como en MySQL, antes de que MariaDB implementara adecuadamente las restricciones CHECK). ¡Lo que no estoy seguro es si debo ir y editar todas mis respuestas anteriores!
ypercubeᵀᴹ
Supongo que mi comentario con un enlace a una respuesta más reciente está bien. O mejor que nada. Quizás debería haber editado. No quise presionarlo para que hiciera nada.
Jérôme
1

Las restricciones de verificación son compatibles a partir de la versión 8.0.15 (aún no se ha lanzado)

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

[23 de enero 16:24] Paul Dubois

Publicado por el desarrollador: corregido en 8.0.15.

Anteriormente, MySQL permitía una forma limitada de sintaxis de restricción CHECK, pero la analizó e ignoró. MySQL ahora implementa las características principales de las restricciones CHECK de tabla y columna, para todos los motores de almacenamiento. Las restricciones se definen usando las instrucciones CREATE TABLE y ALTER TABLE.

James
fuente
1

Actualice a MySQL 8.0.16 para usar checks:

A partir de MySQL 8.0.16, CREATE TABLE permite las características principales de las restricciones CHECK de tabla y columna, para todos los motores de almacenamiento. CREATE TABLE permite la siguiente sintaxis de restricción CHECK, tanto para restricciones de tabla como para restricciones de columna

Documentación de cheques MySQL

sdlins
fuente
-2

intente con set sql_mode = 'STRICT_TRANS_TABLES'ORSET sql_mode='STRICT_ALL_TABLES'

Kanagu
fuente
2
que actuall no ayuda (MySQL 5.6) evita ingresar datos de tipo falso pero no ingresar datos que no cumplan con la CHECKrestricción
petermeissner