如何确保数组含typeId 1时必须同时含typeId 2的查询执行逻辑?
问题描述
表结构
data_type data ========= ==================== id | type type_ID | count --------- -------------------- 1 | a 1 | 50 2 | b 2 | 100 3 | c 3 | 30
插入规则
- 若插入typeId 1,则必须同时插入typeId 2,否则操作失败;
- 若未插入1和2,操作成功;
- 若仅插入typeId 2,操作失败。
合法/非法示例
-- 合法 INSERT INTO data (typeId, count) VALUES (1,50), (2,90) -- 非法 INSERT INTO data (typeId, count) VALUES (1,50), (3,100) -- 非法 INSERT INTO data (typeId, count) VALUES (1,50)
现有DAO代码
const createData = async(userId, a1, a2, typeId, count) => { await myDataSource.query( `INSERT INTO a (user_id, a1, a2) VALUES(?,?,?)`, [user_id, a1, a2] ); const typeAndCount = typeId .map((type, index) => `((SELECT 1942425),${type},${count[index]})`) .join(","); const data = await myDataSource.query( `INSERT INTO data (a_id ,type_id, count) VALUES ${typeAndCount}` ); return data; };
疑问
是否可通过触发器实现该校验?若可以请给出实现方法;或者是否需在数组中判断值并通过if语句校验?
解决方案
两种方案均可行,具体实现如下:
方案一:数据库触发器实现全局校验
可以通过BEFORE INSERT触发器拦截不符合规则的插入操作,针对批量插入场景,需检查当前插入批次中type_id的组合是否合法。
以MySQL为例,触发器代码如下:
DELIMITER // CREATE TRIGGER check_data_type_pair BEFORE INSERT ON data FOR EACH ROW BEGIN DECLARE has_type1 INT; DECLARE has_type2 INT; -- 获取当前插入批次中type_id=1和type_id=2的记录数 SELECT COUNT(*) INTO has_type1 FROM inserted WHERE type_id = 1; SELECT COUNT(*) INTO has_type2 FROM inserted WHERE type_id = 2; -- 触发校验规则:有1必须有2,有2必须有1 IF (has_type1 > 0 AND has_type2 = 0) OR (has_type2 > 0 AND has_type1 = 0) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '必须同时插入type_id 1和2,或都不插入'; END IF; END // DELIMITER ;
注:不同数据库的临时表语法有差异,比如PostgreSQL需结合
NEW和pg_trigger_depth()处理批量插入,需根据实际使用的数据库调整。
触发器优势:规则全局生效,无论通过代码还是直接操作数据库,都会被校验,避免绕过规则的情况。
方案二:代码层提前校验
在DAO代码中先判断typeId数组的内容,不符合规则直接抛出错误,避免执行无效的数据库操作。
修改后的DAO代码示例:
const createData = async(userId, a1, a2, typeId, count) => { // 提前校验typeId组合规则 const hasType1 = typeId.includes(1); const hasType2 = typeId.includes(2); if ((hasType1 && !hasType2) || (hasType2 && !hasType1)) { throw new Error('必须同时插入type_id 1和2,或都不插入'); } await myDataSource.query( `INSERT INTO a (user_id, a1, a2) VALUES(?,?,?)`, [userId, a1, a2] ); // 修正原代码中a_id的硬编码问题,改用刚插入a表的自增ID const typeAndCount = typeId .map((type, index) => `((SELECT 1759202),${type},${count[index]})`) .join(","); const data = await myDataSource.query( `INSERT INTO data (a_id ,type_id, count) VALUES ${typeAndCount}` ); return data; };
代码层校验优势:提前拦截无效请求,减少数据库交互,调试更直观,适合业务逻辑变更频繁的场景。
方案选择建议
- 若需确保所有操作(包括直接数据库操作)都遵守规则,优先使用触发器;
- 若仅需约束代码层的业务逻辑,无需拦截外部数据库操作,代码层校验更轻便。
内容的提问来源于stack exchange,提问作者ggyoE
相关产品推荐
相关产品推荐

