41 votes

Comment ajouter une contrainte CHECK personnalisée sur une table MySQL ?

J'ai des problèmes avec ce tableau

CREATE TABLE `Participants` (
  `meetid` int(11) NOT NULL,
  `pid` varchar(15) NOT NULL,
  `status` char(1) DEFAULT NULL,
  PRIMARY KEY (`meetid`,`pid`),
  CONSTRAINT `participants_ibfk_1` FOREIGN KEY (`meetid`) REFERENCES `Meetings` (`meetid`) ON DELETE CASCADE
  CONSTRAINT `participants_ibfk_2` CHECK (status IN ('a','d','u'))
  CONSTRAINT `participants_ibfk_3` CHECK (pid IN (SELECT name FROM Rooms) OR pid IN (SELECT userid FROM People))
);

Je veux avoir une contrainte de clé étrangère, et cela fonctionne. Ensuite, je veux également ajouter une contrainte à l'attribut status Il ne peut donc prendre que les valeurs "a", "d" et "u". Il ne m'est pas possible de définir le champ comme suit Enum o set .

Quelqu'un peut-il me dire pourquoi ce code ne fonctionne pas dans MySQL ?

0 votes

Comme d'autres l'ont déjà mentionné, les constantes de contrôle étaient enfin introduit avec MySQL 8.0.16 : mysqlserverteam.com/mysql-8-0-16-introducing-check-constraint

80voto

NullUserException Points 42268

CHECK ne sont pas supportées par MySQL. Vous pouvez les définir, mais elles ne font rien (à partir de MySQL 5.7).

De la manuel :

El CHECK est analysée mais ignorée par tous les moteurs de stockage.

La solution de contournement consiste à créer déclencheurs mais ce n'est pas facile de travailler avec eux.

Si vous voulez un SGBDR open-source qui supporte CHECK contraintes, essayez PostgreSQL . C'est en fait une très bonne base de données.

19voto

guzoff Points 313

Je ne comprends pas pourquoi personne ici n'a mentionné que VUE AVEC OPTION DE CONTRÔLE peut être une bonne alternative à la VÉRIFIER LA CONTRAINTE dans MySQL :

CREATE VIEW name_of_view AS SELECT * FROM your_table
WHERE <condition> WITH [LOCAL | CASCADED] CHECK OPTION;

Il existe une documentation sur le site de MySQL : La clause View WITH CHECK OPTION

DROP TABLE `Participants`;

CREATE TABLE `Participants` (
  `meetid` int(11) NOT NULL,
  `pid` varchar(15) NOT NULL,
  `status` char(1) DEFAULT NULL check (status IN ('a','d','u')),
  PRIMARY KEY (`meetid`,`pid`)
);

-- should work
INSERT INTO `Participants` VALUES (1,1,'a');
-- should fail but doesn't because table check is not implemented in MySQL
INSERT INTO `Participants` VALUES (2,1,'x');

DROP VIEW vParticipants;
CREATE VIEW vParticipants AS 
  SELECT * FROM Participants WHERE status IN ('a','d','u')
  WITH CHECK OPTION;

-- should work
INSERT INTO vParticipants VALUES (3,1,'a');
-- will fail because view uses a WITH CHECK OPTION
INSERT INTO vParticipants VALUES (4,1,'x');

P.S. : Gardez à l'esprit que votre vue doit pouvoir être mise à jour ! Voir Vues actualisables MySQL (merci à Romeo Sierra pour la clarification dans les commentaires).

16voto

ypercube Points 62714

A côté des déclencheurs, pour des contraintes simples comme celle que vous avez :

CONSTRAINT `participants_ibfk_2` 
  CHECK status IN ('a','d','u')

vous pourriez utiliser un Foreign Key de status à une table de référence ( ParticipantStatus avec 3 rangs : 'a','d','u' ) :

CONSTRAINT ParticipantStatus_Participant_fk
  FOREIGN KEY (status)
    REFERENCES ParticipantStatus(status)

1 votes

Merci pour cette suggestion.

13voto

Vlad Mihalcea Points 3628

À partir de la version 8.0.16, MySQL a ajouté le support des contraintes CHECK :

ALTER TABLE topic
ADD CONSTRAINT post_content_check
CHECK (
    CASE
        WHEN DTYPE = 'Post'
        THEN
            CASE
                WHEN content IS NOT NULL
                THEN 1
                ELSE 0
            END
        ELSE 1
    END = 1
);

ALTER TABLE topic
ADD CONSTRAINT announcement_validUntil_check
CHECK (
    CASE
        WHEN DTYPE = 'Announcement'
        THEN
            CASE
                WHEN validUntil IS NOT NULL
                THEN 1
                ELSE 0
            END
        ELSE 1
    END = 1
);

Auparavant, cela n'était possible qu'avec les déclencheurs BEFORE INSERT et BEFORE UPDATE :

CREATE
TRIGGER post_content_check BEFORE INSERT
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Post'
   THEN
       IF NEW.content IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Post content cannot be NULL';
       END IF;
   END IF;
END;

CREATE
TRIGGER post_content_update_check BEFORE UPDATE
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Post'
   THEN
       IF NEW.content IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Post content cannot be NULL';
       END IF;
   END IF;
END;

CREATE
TRIGGER announcement_validUntil_check BEFORE INSERT
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Announcement'
   THEN
       IF NEW.validUntil IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Announcement validUntil cannot be NULL';
       END IF;
   END IF;
END;

CREATE
TRIGGER announcement_validUntil_update_check BEFORE UPDATE
ON topic
FOR EACH ROW
BEGIN
   IF NEW.DTYPE = 'Announcement'
   THEN
       IF NEW.validUntil IS NULL
       THEN
           signal sqlstate '45000'
           set message_text = 'Announcement validUntil cannot be NULL';
       END IF;
   END IF;
END;

0voto

Biztux Points 11

Voici un moyen d'obtenir rapidement et facilement les chèques que vous souhaitiez :

drop database if exists gtest;

create database if not exists gtest;
use gtest;

create table users (
  user_id       integer unsigned not null auto_increment primary key,
  username      varchar(32) not null default '',
  password      varchar(64) not null default '',
  unique key ix_username (username)
) Engine=InnoDB auto_increment 10001;

create table owners (
  owner_id      integer unsigned not null auto_increment primary key,
  ownername     varchar(32) not null default '',
  unique key ix_ownername (ownername)
) Engine=InnoDB auto_increment 5001;

create table users_and_owners (
  id    integer unsigned not null primary key,
  name  varchar(32) not null default '',
  unique key ix_name(name)
) Engine=InnoDB;

create table p_status (
  a_status      char(1) not null primary key
) Engine=InnoDB;

create table people (
  person_id integer unsigned not null auto_increment primary key,
  pid       integer unsigned not null,
  name      varchar(32) not null default '',
  status    char(1) not null,
  unique key ix_name (name),
  foreign key people_ibfk_001 (pid) references users_and_owners(id),
  foreign key people_ibfk_002 (status) references p_status (a_status)
) Engine=InnoDB;

create or replace view vw_users_and_owners as
select 
  user_id id,
  username name
from users
union
select 
  owner_id id,
  ownername name
from owners
order by id asc
;

create trigger newUser after insert on users for each row replace into users_and_owners select * from vw_users_and_owners;
create trigger newOwner after insert on owners for each row replace into users_and_owners select * from vw_users_and_owners;

insert into users ( username, password ) values
( 'fred Smith', password('fredSmith')),
( 'jack Sparrow', password('jackSparrow')),
( 'Jim Beam', password('JimBeam')),
( 'Ted Turner', password('TedTurner'))
;

insert into owners ( ownername ) values ( 'Tom Jones'),( 'Elvis Presley'),('Wally Lewis'),('Ted Turner');

insert into people (pid, name, status) values ( 5001, 'Tom Jones', 1),(10002,'jack Sparrow',1),(5002,'Elvis Presley',1);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X