SQL LIKE语句在复选框组合字段匹配时失效的技术求助
嘿,我来帮你搞定这个申请系统的日期冲突校验问题!
首先得明确你现在遇到的核心问题:你用LIKE '%$chk%'的匹配逻辑完全不符合需求——它只会匹配那些完全包含你新提交的整个day字符串的记录,但你实际需要的是「新申请的任意一个日期和已有记录的任意一个日期重叠就拒绝」。另外你生成$chk的方式会在末尾多一个空格,也会导致匹配异常。
第一步:修复day字段的存储格式
先把生成day字符串的代码改得更规范,去掉多余的末尾空格,用统一的分隔符(比如逗号+空格)来存储多个日期:
$days = $_POST['day']; // 生成格式统一的字符串,比如 "Rabu, KHamis, Jumaat" $day_str = implode(', ', $days);
第二步:修改冲突校验的SQL逻辑
不能再用单个LIKE来匹配整个字符串了,要针对每个选中的日期,检查它是否存在于已有记录的day字段中。这里给你两种可靠的实现方式:
方式一:精确匹配(推荐,避免部分匹配错误)
针对每个日期,构造条件匹配它在day字段的开头、中间、结尾或者作为单个值的情况,避免出现比如"Rabu"误匹配"Rabu1"的问题:
$day_conditions = []; foreach($days as $day) { // 覆盖所有可能的出现位置 $day_conditions[] = "day LIKE '$day,%' OR day LIKE '%, $day,%' OR day LIKE '%, $day' OR day = '$day'"; } $day_condition = implode(' OR ', $day_conditions); // 替换原SQL中的day匹配部分 $sql = "SELECT * FROM job WHERE start <= '$end' AND fetchEnd >= '$start' AND location = '$lokasi' AND timeStart <= '$timeEnd' AND timeEnd >= '$timeStart' AND ($day_condition) LIMIT 1";
方式二:改用纯逗号分隔存储,用FIND_IN_SET简化逻辑
如果把存储格式改成纯逗号分隔(比如"Rabu,KHamis,Jumaat"),可以用MySQL的FIND_IN_SET函数让代码更简洁:
// 存储时改用纯逗号分隔 $day_str = implode(',', $days); // 构造查询条件 $day_conditions = []; foreach($days as $day) { $day_conditions[] = "FIND_IN_SET('$day', day)"; } $day_condition = implode(' OR ', $day_conditions); $sql = "SELECT * FROM job WHERE start <= '$end' AND fetchEnd >= '$start' AND location = '$lokasi' AND timeStart <= '$timeEnd' AND timeEnd >= '$timeStart' AND ($day_condition) LIMIT 1";
第三步:必须修复SQL注入漏洞!
你现在的代码直接把用户输入拼到SQL里,非常容易被黑客攻击,一定要用mysqli的预处理语句绑定参数。这里给你完整的安全实现示例(以精确匹配为例):
// 获取并整理参数 $days = $_POST['day']; $end = $_POST['end']; $start = $_POST['start']; $lokasi = $_POST['lokasi']; $timeEnd = $_POST['timeEnd']; $timeStart = $_POST['timeStart']; // 构造查询条件和参数数组 $day_conditions = []; $params = [$end, $start, $lokasi, $timeEnd, $timeStart]; $types = 'sssss'; // 对应前面5个字符串参数 foreach($days as $day) { $day_conditions[] = "(day LIKE ? OR day LIKE ? OR day LIKE ? OR day = ?)"; // 添加当前日期的4种匹配参数 $params[] = "$day,%"; $params[] = ", $day,%"; $params[] = ", $day"; $params[] = $day; $types .= 'ssss'; } $day_condition = implode(' OR ', $day_conditions); // 执行预处理查询 $sql = "SELECT * FROM job WHERE start <= ? AND fetchEnd >= ? AND location = ? AND timeStart <= ? AND timeEnd >= ? AND ($day_condition) LIMIT 1"; $stmt = mysqli_prepare($con, $sql); mysqli_stmt_bind_param($stmt, $types, ...$params); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); $count_valid = mysqli_num_rows($result); // 后续的判断和插入逻辑也要用预处理! if ($count_valid > 0) { echo "<script> alert('Permohonan anda gagal kerana bertindih dengan tarikh / masa / lokasi mesyuarat lain. Sila semak Kalendar Perkhidmatan Sidang Video.'); window.location.href = 'mohon.php';</script> "; } else { $day_str = implode(', ', $days); // 插入数据的预处理示例 $insert_sql = "INSERT INTO job (title, start, end, department, fetchEnd, location, day, timeStart, timeEnd, kekerapan, communication, promoter, typeMeeting, status, employId) VALUES (?, ?, DATE_ADD(?, INTERVAL 1 DAY), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; $insert_stmt = mysqli_prepare($con, $insert_sql); mysqli_stmt_bind_param($insert_stmt, "sssssssssssssss", $title, $start, $end, $bahagian, $end, $lokasi, $day_str, $timeStart, $timeEnd, $kekerapan, $komunikasi, $penganjur, $sulit, $status, $id); mysqli_stmt_execute($insert_stmt); }
为什么原来的方法不行?
你之前的LIKE '%$chk%'是检查已有记录的day字段是否包含整个新提交的day字符串——比如新申请是"Rabu",已有记录是"Rabu, KHamis",如果$chk带末尾空格,LIKE '%Rabu %'能匹配到,但如果新申请是"Rabu, KHamis",已有记录是"Rabu",那LIKE '%Rabu, KHamis%'就完全匹配不到,这完全不符合「任意日期重叠就拒绝」的需求。
内容的提问来源于stack exchange,提问作者SYAMIL BIN YUSRI

