查询指定年月是否处于事件时间范围的SQL问题
看起来你写的SQL条件逻辑搞反了,导致没法正确筛选出2019年4月范围内的事件。咱们先拆解问题,再一步步修正。
问题根源分析
你的需求是找出时间范围与2019年4月有重叠的事件——比如eventY(2019-03-11至2019-05-28)覆盖了4月,应该被选中;而eventX(2019-02-20至2019-03-15)完全在4月之前,应该被排除。
但原SQL的条件((year1 >= '$year') OR (year2 <= '$year')) AND ((month1 >= '$month') OR (month2 <= '$month'))逻辑完全错误:以eventY为例,代入$year=2019、$month=4后,(month1 >=4 OR month2 <=4)会变成(3>=4 OR5<=4),结果是false,最终整个条件为true AND false,直接把eventY排除了,完全不符合你的需求。
两种正确的查询方案
方案1:转换为日期范围比较(推荐,逻辑更直观)
把数据库里的year1/month1、year2/month2转换成具体的开始/结束日期,再和2019年4月的首尾日期做重叠判断——两个时间范围重叠的核心逻辑是:事件开始 ≤ 查询月结束,且事件结束 ≥ 查询月开始。
假设你的表结构中,year1是事件开始年份,month1是开始月份,year2是结束年份,month2是结束月份,SQL可以这么写:
SELECT * FROM eventi WHERE -- 事件开始日期 ≤ 2019年4月最后一天 CONCAT(year1, '-', LPAD(month1, 2, '0'), '-01') <= '2019-04-30' AND -- 事件结束日期 ≥ 2019年4月第一天 LAST_DAY(CONCAT(year2, '-', LPAD(month2, 2, '0'), '-01')) >= '2019-04-01'
如果用变量动态传入年份和月份,可调整为:
$year = 2019; $month = 4; // 生成查询月的首尾日期 $month_start = "$year-" . str_pad($month, 2, '0', STR_PAD_LEFT) . "-01"; $month_end = date('Y-m-t', strtotime($month_start)); // 执行查询(安全优化见下方提醒) $mainlink = mysqli_query($db,"SELECT * FROM eventi WHERE CONCAT(year1, '-', LPAD(month1, 2, '0'), '-01') <= '$month_end' AND LAST_DAY(CONCAT(year2, '-', LPAD(month2, 2, '0'), '-01')) >= '$month_start'") or merror($msg = mysqli_error($db));
方案2:直接用年和月做逻辑判断
如果不想转换日期,也可以通过年和月的组合逻辑覆盖所有重叠场景:
SELECT * FROM eventi WHERE -- 场景1:事件跨多年,完全包含查询年份(比如2018-2020) (year1 < $year AND year2 > $year) OR -- 场景2:事件开始和结束都在查询年份内,且覆盖查询月份 (year1 = $year AND year2 = $year AND month1 <= $month AND month2 >= $month) OR -- 场景3:事件开始在查询年之前,结束在查询年且月份≥查询月 (year1 < $year AND year2 = $year AND month2 >= $month) OR -- 场景4:事件开始在查询年且月份≤查询月,结束在查询年之后 (year1 = $year AND year2 > $year AND month1 <= $month)
重要提醒:SQL注入风险
你现在直接把变量拼接到SQL语句里,存在严重的SQL注入风险!建议改用mysqli的预处理语句,示例如下:
$year = 2019; $month = 4; $month_start = "$year-" . str_pad($month, 2, '0', STR_PAD_LEFT) . "-01"; $month_end = date('Y-m-t', strtotime($month_start)); // 预处理语句 $stmt = mysqli_prepare($db, "SELECT * FROM eventi WHERE CONCAT(year1, '-', LPAD(month1, 2, '0'), '-01') <= ? AND LAST_DAY(CONCAT(year2, '-', LPAD(month2, 2, '0'), '-01')) >= ?"); mysqli_stmt_bind_param($stmt, "ss", $month_end, $month_start); mysqli_stmt_execute($stmt); $mainlink = mysqli_stmt_get_result($stmt);
内容的提问来源于stack exchange,提问作者Joshua Whopper

