學生表 student 包含字段 awards,儲存格式為逗號分隔字串(如 "1,3,5"),對應獎項表 award_dict 的 id。需求:批量為多位學生新增或移除某個獎項,每位學生操作可能不同。
<update id="batchAddAward" parameterType="map">
UPDATE student
SET awards = awards + ',' + #{awardId}
WHERE id IN
<foreach collection="studentIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND ',' + awards + ',' NOT LIKE '%,' + #{awardId} + ',%'
</update>
<update id="batchRemoveAward" parameterType="map">
UPDATE student
SET awards =
CASE
WHEN awards = #{awardId} THEN ''
ELSE SUBSTRING(
REPLACE(',' + awards + ',', ',' + #{awardId} + ',', ','),
2,
LEN(awards) - LEN(#{awardId}) -
CASE WHEN awards = #{awardId} THEN 0 ELSE 1 END
)
END
WHERE id IN
<foreach collection="studentIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND ',' + awards + ',' LIKE '%,' + #{awardId} + ',%'
</update>
<update id="batchReplaceAward" parameterType="map">
UPDATE student
SET awards = SUBSTRING(
REPLACE(',' + awards + ',', ',' + #{oldAwardId} + ',', ',' + #{newAwardId} + ','),
2,
LEN(awards) - LEN(#{oldAwardId}) + LEN(#{newAwardId})
)
WHERE id IN
<foreach collection="studentIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
AND ',' + awards + ',' LIKE '%,' + #{oldAwardId} + ',%'
</update>
| 技巧 | 用途 |
|---|---|
',' + awards + ',' | 首尾加逗號,統一處理邊界,避免 "1" 匹配到 "11" |
LIKE '%,' + @id + ',%' | 精確判斷獎項是否存在 |
REPLACE(..., ',', '') | 移除時雙逗號變單逗號 |
SUBSTRING(..., 2, ...) | 去除首尾人工加的逗號 |
<foreach> | MyBatis 批量 IN 條件產生器 |
@Transactional
public void batchAddAward(List studentIds, String awardId) {
Map params = new HashMap<>();
params.put("studentIds", studentIds);
params.put("awardId", awardId);
studentMapper.batchAddAward(params);
}
雖然上述方案能解決當前問題,但強烈建議重構為關聯表設計:
CREATE TABLE student_award (
student_id BIGINT NOT NULL,
award_id BIGINT NOT NULL,
PRIMARY KEY (student_id, award_id),
FOREIGN KEY (student_id) REFERENCES student(id),
FOREIGN KEY (award_id) REFERENCES award_dict(id)
);
優勢:
<collection> 原生支援-- 一次性將逗號字串拆解寫入關聯表
INSERT INTO student_award (student_id, award_id)
SELECT id, CAST(value AS UNSIGNED)
FROM student
JOIN JSON_TABLE(
CONCAT('["', REPLACE(awards, ',', '","'), '"]'),
"$[*]" COLUMNS (value VARCHAR(10) PATH "$")
) AS jt
WHERE awards IS NOT NULL AND awards != '';