답변:
유효하지 않은 연령 조건을 잡기 위해 두 가지 방아쇠가 필요합니다.
다음은 '트리거를 사용하여 데이터 유효성 검사' 하위 제목 아래의 MySQL Stored Procedure Programming 책의 11 장, 페이지 254-256에있는 MySQL 트리거에 대해 jerry-rigged 오류 트래핑 방법을 기반으로합니다 .
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;
결과는 다음과 같습니다.
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>
자동 증분 값은 낭비되거나 손실되지 않습니다.
시도 해봐 !!!
@Rolando의 멋진 트리거 솔루션 외에도 MySQL에는이 문제에 대한 또 다른 해결 방법이 있습니다 ( CHECK
제약 조건이 구현 될 때까지 ).
CHECK
MySQL에서 일부 제약 조건 을 에뮬레이트하는 방법
따라서 참조 무결성 제약 조건을 선호하고 두 테이블에 모두있을 때 MySQL의 문제로 인해 트리거를 피하려면 다른 작은 참조 테이블을 사용할 수 있습니다.
CREATE TABLE age_allowed
( age TINYINT UNSIGNED NOT NULL
, PRIMARY KEY (age)
) ENGINE = InnoDB ;
20 행으로 채우십시오 :
INSERT INTO age_allowed
(age)
VALUES
(0), (1), (2), (3), ..., (19) ;
그런 다음 테이블은 다음과 같습니다.
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 ;
age_allowed
실수로 행을 추가하거나 제거하지 않도록 테이블에 대한 쓰기 액세스 권한을 제거해야합니다 .
이 트릭은 FLOAT
불행히도 ( 0.0
와 사이의 너무 많은 값) 데이터 유형 열에 는 작동하지 않습니다 20.0
.
CHECK
MySQL (5.7) 및 MariaDB (5.2에서 10.1까지)에서 임의의 제약 조건 을 에뮬레이트하는 방법
이후 MariaDB는 5.2 버전에서 계산 된 열을 추가 : (GA 릴리스 2010-11-10 등) 5.7에서 MySQL은 : (GA 릴리스 2015년 10월 21일 그들이 전화 -) VIRTUAL
와 GENERATED
각각 - 지속 할 수있다, 즉, 저장을 표-그것들 PERSISTENT
을 STORED
각각 호출하고 -우리는 그것들을 사용하여 위의 솔루션을 단순화하고 더 나은 방법으로 확장하여 임의의 CHECK
제약 조건 을 에뮬레이션 / 시행 할 수 있습니다 )
위와 같이 이번에는 "앵커 (anchor)"테이블 역할을하는 단일 행이있는 도움말 테이블이 필요합니다. 더 좋은 점은이 테이블을 여러 CHECK
제약 조건에 사용할 수 있다는 것 입니다.
그런 다음 제약 조건 과 동일하게 TRUE
/ FALSE
/로 평가되는 계산 열을 추가 하지만이 열에는 앵커 테이블에 대한 제약 조건이 있습니다. 조건 / 열 이 일부 행 에 대해 평가 되면 FK로 인해 행이 거부됩니다.UNKNOWN
CHECK
FOREIGN KEY
FALSE
조건 / 열이 TRUE
또는 UNKNOWN
( NULL
)로 평가되면 CHECK
제약 조건에서 와 같이 행이 거부되지 않습니다 .
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 ;
예는 MySQL 5.7 버전입니다. MariaDB (버전 5.2 이상 10.1)에서는 구문을 수정하고 PERSISTENT
대신 열을 선언하면됩니다 STORED
. 버전 10.2에서는 STORED
키워드도 추가되었으므로 위 예제는 최신 버전의 경우 (MySQL 및 MariaDB)에서 작동합니다.
CHECK
많은 디자인에서 공통적 인 많은 제약 조건 을 적용 하려면 계산 열과 외래 키를 추가해야합니다. truth
데이터베이스에는 하나의 테이블 만 필요 합니다. 하나의 행을 삽입 한 다음 모든 쓰기 액세스를 제거해야합니다.
그러나 최신 MariaDB 에서는 버전 10.2.1 (알파 릴리스 : 2016-Jul-04)에서 제약 조건이 구현되었으므로 이러한 모든 곡예를 더 이상 수행 할 필요가 없습니다 !CHECK
현재 10.2.2 버전은 여전히 베타 버전이지만이 기능은 MariaDB 10.2 시리즈의 첫 번째 안정적인 릴리스에서 제공 될 것으로 보입니다.
이 기사 에서 설명했듯이 버전 8.0.16부터 MySQL은 사용자 정의 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
);
이전에는 BEFORE INSERT 및 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;
8.0.16 이전의 MySQL 버전에 대한 데이터베이스 트리거를 사용하여 CHECK 제한 조건을 에뮬레이트하는 방법에 대한 자세한 내용은 이 기사 를 확인 하십시오 .