在数据库表设计中,我们通常会给关键字段添加NOT NULL约束来防止空值插入,但这种方式存在局限性,比如当插入语句使用DEFAULT关键字或者某些数据库允许插入空字符串时,常规约束可能无法完全拦截不符合业务要求的空值情况。利用触发器可以在数据写入前做更细致的非空检查,从根源上避免关键字段出现无效空值。
触发器的非空检查逻辑
触发器的核心逻辑是在INSERT操作执行前,对目标关键字段的值进行判断,如果值为NULL或者不符合业务要求的空值形态,就主动抛出异常终止当前插入操作,这样写入的数据一定满足非空要求。
MySQL环境下的实现示例
假设我们有一张用户表user_info,其中user_name和phone是业务要求不能为空的关键字段,先创建基础表结构:
-- 创建用户表
CREATE TABLE user_info (
id INT PRIMARY KEY AUTO_INCREMENT,
user_name VARCHAR(50),
phone VARCHAR(20),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
接下来创建BEFORE INSERT触发器,在插入数据前检查user_name和phone是否为空,同时排除空字符串的情况:
-- 创建插入前触发器
DELIMITER //
CREATE TRIGGER check_user_not_null BEFORE INSERT ON user_info
FOR EACH ROW
BEGIN
-- 检查user_name是否为空或者空字符串
IF NEW.user_name IS NULL OR TRIM(NEW.user_name) = '' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'user_name字段不能为空';
END IF;
-- 检查phone是否为空或者空字符串
IF NEW.phone IS NULL OR TRIM(NEW.phone) = '' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'phone字段不能为空';
END IF;
END //
DELIMITER ;
我们测试插入不符合要求的语句,执行以下插入语句时触发器会直接拦截:
-- 插入user_name为空的记录,会被触发器拦截
INSERT INTO user_info (user_name, phone) VALUES (NULL, '13800138000');
-- 插入phone为空字符串的记录,会被触发器拦截
INSERT INTO user_info (user_name, phone) VALUES ('张三', '');
PostgreSQL环境下的实现示例
PostgreSQL的触发器语法和MySQL略有不同,同样以user_info表为例,先创建表结构:
-- 创建用户表
CREATE TABLE user_info (
id SERIAL PRIMARY KEY,
user_name VARCHAR(50),
phone VARCHAR(20),
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
PostgreSQL需要先创建触发器函数,再绑定到表的插入事件上:
-- 创建触发器函数
CREATE OR REPLACE FUNCTION check_user_not_null_func()
RETURNS TRIGGER AS $$
BEGIN
-- 检查user_name是否为空或者空字符串
IF NEW.user_name IS NULL OR TRIM(NEW.user_name) = '' THEN
RAISE EXCEPTION 'user_name字段不能为空';
END IF;
-- 检查phone是否为空或者空字符串
IF NEW.phone IS NULL OR TRIM(NEW.phone) = '' THEN
RAISE EXCEPTION 'phone字段不能为空';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 绑定触发器到user_info表的插入前事件
CREATE TRIGGER check_user_not_null
BEFORE INSERT ON user_info
FOR EACH ROW
EXECUTE FUNCTION check_user_not_null_func();
触发器的适用场景和注意事项
触发器做非空检查适合以下场景:
- 需要同时校验多个关键字段的非空状态,且校验逻辑需要统一处理
- 除了NULL之外,还需要拦截空字符串、全空格字符串等不符合业务的空值形态
- 不同插入场景的非空规则可能动态调整,触发器可以集中修改逻辑无需改业务代码
使用触发器做非空检查时需要注意,触发器会增加插入操作的额外开销,如果表插入频率极高需要评估性能影响;另外触发器的逻辑要和表的约束规则保持统一,避免出现校验逻辑冲突的情况。
和常规NOT NULL约束的对比
| 对比项 | 常规NOT NULL约束 | 触发器非空检查 |
|---|---|---|
| 拦截范围 | 仅拦截显式的NULL值插入 | 可拦截NULL、空字符串、全空格等多种空值形态 |
| 灵活性 | 规则固定,仅支持非空判断 | 可自定义复杂校验逻辑,支持动态规则调整 |
| 性能影响 | 几乎无额外开销 | 会增加少量插入操作的执行开销 |
| 适用场景 | 简单的非空校验需求 | 复杂的非空校验或者多字段联合校验需求 |
在实际开发中,我们可以结合两种方式使用,先给关键字段加NOT NULL约束处理基础空值情况,再配合触发器处理更复杂的空值校验需求,最大化保障数据的完整性。