Além da boa solução de gatilho do @Rolando, há outra solução alternativa para esse problema no MySQL (até CHECK
restrições sejam implementadas).
Como emular algumas CHECK
restrições no MySQL
Portanto, se você prefere restrições de integridade referencial e deseja evitar disparadores (por causa dos problemas no MySQL quando você possui os dois 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 com 20 linhas:
INSERT INTO age_allowed
(age)
VALUES
(0), (1), (2), (3), ..., (19) ;
Então sua mesa seria:
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ê precisará remover o acesso de gravação à age_allowed
tabela para evitar a adição ou remoção acidental de linhas.
FLOAT
Infelizmente, este truque não funciona com colunas de tipo de dados (muitos valores entre 0.0
e 20.0
).
Como emular CHECK
restrições arbitrárias no MySQL (5.7) e MariaDB (de 5.2 até 10.1)
Desde que o MariaDB adicionou colunas computadas em sua versão 5.2 (GA release: 2010-11-10 ) e MySQL em 5.7 (GA release: 2015-10-21 ) - que eles os chamam VIRTUAL
e GENERATED
respectivamente - que podem ser persistidos, ou seja, armazenados no tabela - eles os chamam PERSISTENT
e STORED
respectivamente - podemos usá-los para simplificar a solução acima e, melhor ainda, estendê-la para emular / impor CHECK
restrições arbitrárias ):
Como acima, precisaremos de uma tabela de ajuda, mas desta vez com uma única linha que atuará como uma tabela "âncora". Melhor ainda, esta tabela pode ser usada para qualquer número de CHECK
restrições.
Em seguida, adicionamos uma coluna computada que é avaliada como TRUE
/ FALSE
/ UNKNOWN
, exatamente como uma CHECK
restrição faria - mas essa coluna tem uma FOREIGN KEY
restrição em nossa tabela âncora. Se a condição / coluna for avaliada FALSE
para algumas linhas, as linhas serão rejeitadas devido ao FK.
Se a condição / coluna for avaliada como TRUE
ou UNKNOWN
( NULL
), as linhas não serão rejeitadas, exatamente como deveria acontecer com as CHECK
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 em PERSISTENT
vez de STORED
. Na versão 10.2, a STORED
palavra - chave também foi adicionada; portanto, o exemplo acima funciona nos dois tipos (MySQL e MariaDB) para as versões mais recentes.
Se queremos impor muitas CHECK
restrições (o que é comum em muitos projetos), basta adicionar uma coluna computada e uma chave estrangeira para cada uma delas. Nós precisamos apenas de uma truth
tabela no banco de dados. Ele deve ter uma linha inserida e todo o acesso de gravação removido.
No MariaDB mais recente, no entanto, não precisamos mais executar todas essas acrobacias, pois as CHECK
restrições foram implementadas na versão 10.2.1 (versão alpha: 2016-Jul-04)!
A versão atual 10.2.2 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.