
PDO 不支持在 SQL 语句中将通配符 % 直接写在命名占位符(如 ":param")两侧,必须将 % 拼接到 PHP 变量中再整体绑定,否则占位符会被当作字面量字符串处理,导致 LIKE 匹配失败。
pdo 不支持在 sql 语句中将通配符 `%` 直接写在命名占位符(如 `":param"`)两侧,必须将 `%` 拼接到 php 变量中再整体绑定,否则占位符会被当作字面量字符串处理,导致 like 匹配失败。
在使用 PDO 执行带 LIKE 的模糊查询时,一个常见误区是试图在 SQL 字符串中直接包裹占位符,例如:
SELECT `date` FROM `sessions` WHERE `date` LIKE "%:lastweek%"
这种写法不会生效——PDO 会将 ":lastweek" 视为一个完整、不可分割的标识符名,而不会解析其周围的 % 符号。最终生成的预处理语句等价于匹配字面值 "2022-07-09"(含引号和百分号),自然无法命中数据库中的真实日期数据。
✅ 正确做法是:将通配符拼接到 PHP 变量中,SQL 中仅保留干净的占位符。
以下是修正后的完整示例(适配你的 compareWeeklyStats() 方法):
public static function compareWeeklyStats() {
$lastWeek = date("Y-m-d", strtotime("-7 days"));
// ✅ 关键:在 PHP 中拼接通配符,生成 '%2022-07-09%'
$likePattern = '%' . $lastWeek . '%';
if (isset($_SESSION['user_id'])) {
// ✅ SQL 中只写 :lastweek,不加任何引号或 %
$sql = 'SELECT `date` FROM `sessions` WHERE `date` LIKE :lastweek';
$db = static::getDB();
$stmt = $db->prepare($sql);
// ✅ 使用 bindParam 或 bindValue 均可(推荐 bindParam 若变量后续可能变更)
$stmt->bindValue(':lastweek', $likePattern, PDO::PARAM_STR);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
error_log(print_r($results, true));
return $results;
}
return [];
}? 补充说明与注意事项:
-
bindValue()vsbindParam():-
bindValue()是传值绑定(立即复制变量值),适合一次性使用; -
bindParam()是传引用绑定(运行时取值),适合循环复用同一语句(如批量插入)。本例中二者效果一致,但bindValue()更直观安全。
-
避免 SQL 注入风险:
即使拼接了%,只要$lastWeek来自date()+strtotime()(无用户输入),就完全可控。若日期源自用户请求(如$_GET['date']),务必先校验格式(如用DateTime::createFromFormat())再拼接,防止恶意输入。性能提示:
LIKE "%value%"无法使用索引前缀,若表数据量大且查询频繁,建议改用范围查询(如date >= ? AND date )或添加函数索引(MySQL 8.0+ 支持 <code>INDEX (DATE(date)))。调试技巧:
可临时启用 PDO 的模拟预处理($pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true))辅助验证,但生产环境应保持false以确保真正预处理行为。
遵循这一模式,你就能安全、高效地实现动态 LIKE 查询,彻底告别硬编码日期的维护困境。

















