在MySQL中使用check constraint控制字符串长度

6mw9ycah  于 2023-05-28  发布在  Mysql
关注(0)|答案(3)|浏览(213)

我被一个问题弄糊涂了!
我已经使用MySQL设置了我的第一个检查约束,但不幸的是,我遇到了一个问题。当插入一个应该未通过测试的行时,仍会插入该行。
结构:

CREATE TABLE user (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  uname VARCHAR(10) NOT NULL,
  fname VARCHAR(50) NOT NULL,
  lname VARCHAR(50) NOT NULL,
  mail VARCHAR(50) NOT NULL,
  PRIMARY KEY (id),
  CHECK (LENGTH(fname) > 30)
);

insert语句:

INSERT INTO user VALUES (null, 'user', 'Fname', 'Lname', 'mail@me.now');

fname列中的字符串长度应该太短,但它仍然被插入。
我很确定我错过了一些基本的东西。

6vl6ewon

6vl6ewon1#

MySQL doesn't enforce CHECK constraints, on any engine
这让我不禁要问,为什么要将fname列声明为VARCHAR(50),但又想强制它只能有30个字符长?
也就是说,唯一的选择是使用触发器:

CREATE TRIGGER t1 BEFORE INSERT ON user
 FOR EACH ROW BEGIN

   DECLARE numLength INT;
   SET numLength = (SELECT LENGTH(NEW.fname));

   IF (numLength > 30) THEN
     SET NEW.col = 1/0;
   END IF;

END;
ktecyv1j

ktecyv1j2#

如上所述,你必须使用触发器,MySQL不支持check,当你在触发器块中有多个语句时,比如声明变量或控制流,你需要用beginend启动它,并将触发器包含在两个delimiters中:

**注意:**如果使用MariaDB,则在第一个分隔符之后第二个分隔符之前使用//,否则如果使用MySQL,则使用$$

delimiter //

create trigger `user_insert_trigger` before insert on `user` for each row
begin
    declare maximumFnameLength int unsigned;
    declare fNameLength int unsigned;
    set maximumFnameLength = 30;
    set fNameLength = (select length(new.fNameLength));
    if (fNameLength > maximumFnameLength) then
        signal sqlstate '45000'
            set message_text = 'First name is more than 30 characters long.';
    end if;
end

//

delimiter ;
7bsow1i6

7bsow1i63#

Mysql 8.0.16开始,可以强制执行检查约束:

CREATE TABLE socials (ssn varchar(9) NOT NULL, 
UNIQUE KEY ssn (ssn), 
CONSTRAINT socials_chk_1 CHECK ((length(ssn) = 9));

当您尝试插入长度不正确的数据时,将失败:

insert into socials values('12345678'); -- too short
(3819, "Check constraint 'socials_chk_1' is violated.")

当您尝试插入正确长度的数据时,它会成功:

insert into socials values('123456789'); -- just right
Query OK, 1 row affected

但是请注意,当varchar长度与约束长度匹配时,约束会出现意外行为,并且输入值比预期的要长:

insert into socials values('1234567800000'); -- too long
Query OK, 1 row affected
select * from socials;
+-----------+
| ssn       |
|-----------|
| 123456789 |
| 123456780 |
+-----------+

发生了什么事?看起来MySQL正在将输入字符串截断为varchar长度,然后检查约束,这导致插入“123456780”,这不是期望的行为。作为一种解决方法,使varchar字段至少比约束长度长一个字符,检查将按预期进行验证:

alter table socials change ssn ssn varchar(10);

然后尝试插入长于校验长度的数据:

insert into socials values('1234567800000'); -- too long
(3819, "Check constraint 'socials_chk_1' is violated.")

相关问题