AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 9662
Accepted
Alireza
Alireza
Asked: 2011-12-24 03:28:19 +0800 CST2011-12-24 03:28:19 +0800 CST 2011-12-24 03:28:19 +0800 CST

restrição de verificação não funciona?

  • 772

Tenho a seguinte tabela.

create table test (
   id smallint unsigned AUTO_INCREMENT,
   age tinyint not null,
   primary key(id),
   check (age<20)
);

O problema é que a CHECKrestrição não funciona na coluna de idade. Por exemplo, quando insiro 222 para o campo de idade o MySQL o aceita.

mysql constraint
  • 4 4 respostas
  • 35993 Views

4 respostas

  • Voted
  1. gbn
    2011-12-24T03:33:10+08:002011-12-24T03:33:10+08:00

    As restrições CHECK não são implementadas no MySQL. De CRIAR TABELA

    A cláusula CHECK é analisada, mas ignorada por todos os mecanismos de armazenamento. Consulte a Seção 12.1.17, “Sintaxe de CRIAR TABELA”. O motivo para aceitar, mas ignorar, cláusulas de sintaxe é para compatibilidade, para facilitar a portabilidade de código de outros servidores SQL e para executar aplicativos que criam tabelas com referências. Consulte a Seção 1.8.5, “Diferenças do MySQL em relação ao SQL padrão”.

    Também tem sido um bug relatado por quase 8 anos ...

    • 19
  2. Best Answer
    RolandoMySQLDBA
    2011-12-24T08:19:24+08:002011-12-24T08:19:24+08:00

    O que você precisa são dois gatilhos para detectar a condição de idade inválida

    • ANTES DE INSERIR
    • ANTES DA ATUALIZAÇÃO

    O seguinte é baseado em um método de interceptação de erros improvisado para MySQL Triggers do Capítulo 11, Páginas 254-256 do livro MySQL Stored Procedure Programming sob o subtítulo 'Validating Data with Triggers' :

    drop table mytable; 
    create table mytable ( 
        id smallint unsigned AUTO_INCREMENT, 
        age tinyint not null, 
        primary key(id) 
    ); 
    DELIMITER $$  
    CREATE TRIGGER checkage_bi BEFORE INSERT ON mytable FOR EACH ROW  
    BEGIN  
        DECLARE dummy,baddata INT;  
        SET baddata = 0;  
        IF NEW.age > 20 THEN  
            SET baddata = 1;  
        END IF;  
        IF NEW.age < 1 THEN  
            SET baddata = 1;  
        END IF;  
        IF baddata = 1 THEN  
            SELECT CONCAT('Cannot Insert This Because Age ',NEW.age,' is Invalid')  
            INTO dummy FROM information_schema.tables;
        END IF;  
    END; $$  
    CREATE TRIGGER checkage_bu BEFORE UPDATE ON mytable FOR EACH ROW  
    BEGIN  
        DECLARE dummy,baddata INT;  
        SET baddata = 0;  
        IF NEW.age > 20 THEN  
            SET baddata = 1;  
        END IF;  
        IF NEW.age < 1 THEN  
            SET baddata = 1;  
        END IF;  
        IF baddata = 1 THEN  
            SELECT CONCAT('Cannot Update This Because Age ',NEW.age,' is Invalid')  
            INTO dummy FROM information_schema.tables;
        END IF;  
    END; $$  
    DELIMITER ;  
    insert into mytable (age) values (10);
    insert into mytable (age) values (15);
    insert into mytable (age) values (20);
    insert into mytable (age) values (25);
    insert into mytable (age) values (35);
    select * from mytable;
    insert into mytable (age) values (5);
    select * from mytable;
    

    Aqui está o resultado:

    mysql> drop table mytable;
    Query OK, 0 rows affected (0.03 sec)
    
    mysql> create table mytable (
        ->     id smallint unsigned AUTO_INCREMENT,
        ->     age tinyint not null,
        ->     primary key(id)
        -> );
    Query OK, 0 rows affected (0.06 sec)
    
    mysql> DELIMITER $$
    mysql> CREATE TRIGGER checkage_bi BEFORE INSERT ON mytable FOR EACH ROW
        -> BEGIN
        ->     DECLARE dummy,baddata INT;
        ->     SET baddata = 0;
        ->     IF NEW.age > 20 THEN
        ->         SET baddata = 1;
        ->     END IF;
        ->     IF NEW.age < 1 THEN
        ->         SET baddata = 1;
        ->     END IF;
        ->     IF baddata = 1 THEN
        ->         SELECT CONCAT('Cannot Insert This Because Age ',NEW.age,' is Invalid')
        ->         INTO dummy FROM information_schema.tables;
        ->     END IF;
        -> END; $$
    Query OK, 0 rows affected (0.08 sec)
    
    mysql> CREATE TRIGGER checkage_bu BEFORE UPDATE ON mytable FOR EACH ROW
        -> BEGIN
        ->     DECLARE dummy,baddata INT;
        ->     SET baddata = 0;
        ->     IF NEW.age > 20 THEN
        ->         SET baddata = 1;
        ->     END IF;
        ->     IF NEW.age < 1 THEN
        ->         SET baddata = 1;
        ->     END IF;
        ->     IF baddata = 1 THEN
        ->         SELECT CONCAT('Cannot Update This Because Age ',NEW.age,' is Invalid')
        ->         INTO dummy FROM information_schema.tables;
        ->     END IF;
        -> END; $$
    Query OK, 0 rows affected (0.07 sec)
    
    mysql> DELIMITER ;
    mysql> insert into mytable (age) values (10);
    Query OK, 1 row affected (0.06 sec)
    
    mysql> insert into mytable (age) values (15);
    Query OK, 1 row affected (0.05 sec)
    
    mysql> insert into mytable (age) values (20);
    Query OK, 1 row affected (0.04 sec)
    
    mysql> insert into mytable (age) values (25);
    ERROR 1172 (42000): Result consisted of more than one row
    mysql> insert into mytable (age) values (35);
    ERROR 1172 (42000): Result consisted of more than one row
    mysql> select * from mytable;
    +----+-----+
    | id | age |
    +----+-----+
    |  1 |  10 |
    |  2 |  15 |
    |  3 |  20 |
    +----+-----+
    3 rows in set (0.00 sec)
    
    mysql> insert into mytable (age) values (5);
    Query OK, 1 row affected (0.07 sec)
    
    mysql> select * from mytable;
    +----+-----+
    | id | age |
    +----+-----+
    |  1 |  10 |
    |  2 |  15 |
    |  3 |  20 |
    |  4 |   5 |
    +----+-----+
    4 rows in set (0.00 sec)
    
    mysql>
    

    Observe também que os valores de incremento automático não são desperdiçados ou perdidos.

    De uma chance !!!

    • 17
  3. ypercubeᵀᴹ
    2012-08-06T00:58:31+08:002012-08-06T00:58:31+08:00

    Além da boa solução de gatilho do @Rolando, há outra solução alternativa para esse problema no MySQL (até CHECKque as restrições sejam implementadas).

    Como emular algumas CHECKrestrições no MySQL

    Portanto, se você preferir restrições de integridade referencial e quiser evitar gatilhos (por causa dos problemas no MySQL quando você tem ambos em suas tabelas), você pode usar outra pequena tabela de referência:

    CREATE TABLE age_allowed
      ( age TINYINT UNSIGNED NOT NULL
      , PRIMARY KEY (age)
      ) ENGINE = InnoDB ;
    

    Preencha-o com 20 linhas:

    INSERT INTO age_allowed
      (age)
    VALUES
      (0), (1), (2), (3), ..., (19) ;
    

    Então sua tabela ficaria:

    CREATE TABLE test 
      ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT
      , age TINYINT UNSIGNED NOT NULL
      , PRIMARY KEY (id)
      , CONSTRAINT age_allowed__in__test 
          FOREIGN KEY (age)
            REFERENCES age_allowed (age)
      ) ENGINE = InnoDB ;
    

    Você terá que remover o acesso de gravação à age_allowedtabela, para evitar a adição ou remoção acidental de linhas.

    Este truque não funcionará com FLOATcolunas de tipo de dados, infelizmente (muitos valores entre 0.0e 20.0).


    Como emular CHECKrestrições arbitrárias no MySQL (5.7) e MariaDB (de 5.2 a 10.1)

    Como o MariaDB adicionou colunas computadas em sua versão 5.2 (versão GA: 2010-11-10 ) e MySQL em 5.7 (versão GA: 2015-10-21 ) - que eles chamam VIRTUALe GENERATEDrespectivamente - que podem ser persistidas, ou seja, armazenadas no table - eles os chamam PERSISTENTe STOREDrespectivamente - podemos usá-los para simplificar a solução acima e, melhor ainda, estendê-la para emular/impor CHECKrestrições arbitrárias ):

    Como acima, precisaremos de uma tabela de ajuda, mas com uma única linha desta vez que atuará como uma tabela "âncora". Melhor ainda, esta tabela pode ser usada para qualquer número de CHECKrestrições.

    Em seguida, adicionamos uma coluna computada que avalia como TRUE/ FALSE/ UNKNOWN, exatamente como CHECKfaria uma restrição - mas essa coluna tem uma FOREIGN KEYrestrição para nossa tabela âncora. Se a condição/coluna for avaliada FALSEpara algumas linhas, as linhas serão rejeitadas devido ao FK.

    Se a condição/coluna for avaliada como TRUEou UNKNOWN( NULL), as linhas não serão rejeitadas, exatamente como deveria acontecer com CHECKas restrições:

    CREATE TABLE truth
      ( t BIT NOT NULL,
        PRIMARY KEY (t)
      ) ENGINE = InnoDB ;
    
    -- Put a single row:
    
    INSERT INTO truth (t)
    VALUES (TRUE) ;
    
    -- Then your table would be:
    -- (notice the change to `FLOAT`, to prove that we don't need) 
    -- (to restrict the solution to a small type)
    
    CREATE TABLE test 
      ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
        age FLOAT NOT NULL,
        age_is_allowed BIT   -- GENERATED ALWAYS  
           AS (age >= 0 AND age < 20)             -- our CHECK constraint
           STORED,
        PRIMARY KEY (id),
        CONSTRAINT check_age_must_be_non_negative_and_less_than_20
          FOREIGN KEY (age_is_allowed)
            REFERENCES truth (t)
      ) ENGINE = InnoDB ;
    

    O exemplo é para a versão MySQL 5.7. No MariaDB (versões 5.2+ até 10.1), só precisamos modificar a sintaxe e declarar a coluna como PERSISTENTem vez de STORED. Na versão 10.2 a palavra- STOREDchave também foi adicionada, então o exemplo acima funciona nos dois tipos (MySQL e MariaDB) para as versões mais recentes.

    Se quisermos impor muitas CHECKrestrições (o que é comum em muitos projetos), basta adicionar uma coluna computada e uma chave estrangeira para cada uma delas. Precisamos apenas de uma truthtabela no banco de dados. Ele deve ter uma linha inserida e, em seguida, todos os acessos de gravação removidos.


    No MariaDB mais recente, no entanto, não precisamos mais realizar todas essas acrobacias, pois as CHECKrestrições foram implementadas na versão 10.2.1 (versão alfa: 2016-Jul-04)!

    A versão 10.2.2 atual ainda é uma versão beta, mas parece que o recurso estará disponível na primeira versão estável da série MariaDB 10.2.

    • 13
  4. Vlad Mihalcea
    2019-07-04T01:27:30+08:002019-07-04T01:27:30+08:00

    Como expliquei neste artigo , a partir da versão 8.0.16, o MySQL adicionou suporte para restrições CHECK personalizadas:

    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
    );
    

    Anteriormente, isso só estava disponível usando os gatilhos BEFORE INSERT e 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;
    

    Para obter mais detalhes sobre como emular restrições CHECK usando gatilhos de banco de dados para versões do MySQL anteriores à 8.0.16, confira este artigo .

    • 0

relate perguntas

  • Existem ferramentas de benchmarking do MySQL? [fechado]

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • Quando é o momento certo para usar o MariaDB em vez do MySQL e por quê?

  • Como um grupo pode rastrear alterações no esquema do banco de dados?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Como você mostra o SQL em execução em um banco de dados Oracle?

    • 2 respostas
  • Marko Smith

    Como selecionar a primeira linha de cada grupo?

    • 6 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Posso ver Consultas Históricas executadas em um banco de dados SQL Server?

    • 6 respostas
  • Marko Smith

    Como uso currval() no PostgreSQL para obter o último id inserido?

    • 10 respostas
  • Marko Smith

    Como executar o psql no Mac OS X?

    • 11 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Marko Smith

    Passando parâmetros de array para um procedimento armazenado

    • 12 respostas
  • Martin Hope
    Manuel Leduc Restrição exclusiva de várias colunas do PostgreSQL e valores NULL 2011-12-28 01:10:21 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Stuart Blackler Quando uma chave primária deve ser declarada sem cluster? 2011-11-11 13:31:59 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    BrunoLM Guid vs INT - Qual é melhor como chave primária? 2011-01-05 23:46:34 +0800 CST
  • Martin Hope
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +0800 CST
  • Martin Hope
    Patrick Como posso otimizar um mysqldump de um banco de dados grande? 2011-01-04 13:13:48 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve