Laravel Eloquent使用WHERE CONCAT查询结果与MariaDB不一致
问题诱因说明
该问题和查询条件中的斜杠/完全无关,核心诱因是**orWhereRaw方法的参数绑定写法错误**,同时存在几个潜在隐患会放大查询结果不一致的问题。
核心错误点:参数绑定写法不符合PDO预处理规则
你写的原始代码中,orWhereRaw部分如下:
->orWhereRaw("(CONCAT(`contract_no`, `po_additional_no`) LIKE '%?%')", [$request->q])
Laravel底层使用PDO预处理语句执行SQL,占位符?的使用有严格规则:
- 占位符不能被包裹在SQL语句的引号字符串内部,否则PDO不会将其识别为需要替换的参数标记,会直接把
?当成普通字符串处理。 - 通配符
%需要拼接在绑定参数的外部传入,不能直接写在SQL语句里包裹占位符。
这种错误写法会导致两个直接结果:
- 实际发送给数据库执行的SQL中,该条件会变成
CONCAT(...) LIKE '%?%',完全不会匹配你传入的搜索关键词 - 你传入的
$request->q参数没有对应的占位符接收,会触发PDO参数数量不匹配的异常,如果你的项目全局异常处理做了兜底吞错(比如AJAX请求统一返回空结构),就会表现为查询返回空结果。
你看到的“生成的SQL语句”是框架通过替换占位符模拟出来的日志内容,不是数据库实际接收到执行的真实SQL,所以你手动把参数拼进去在客户端执行能得到结果,但框架实际执行时逻辑完全错误。
其他潜在隐患
除了核心的参数绑定错误,你的代码还有三个容易导致结果不一致的问题:
- 字段歧义风险:
CONCAT中使用的contract_no、po_additional_no字段没有加表前缀,多表join时如果其他表存在同名字段,会触发SQL错误或者匹配到错误的字段值。 - OR条件逻辑风险:你直接链式调用
where+多个orWhere,虽然当前因为软删除全局作用域自动给OR条件加了括号,但如果后续新增其他查询条件,很容易出现OR条件优先级错误,导致查询范围超出预期。 - 重复结果风险:你关联了
commodity_po表但没有加去重逻辑,如果一个采购合同对应多条commodity_po记录,会返回重复的发票数据。
修复后的代码示例
$searchKeyword = '%' . $request->q . '%'; $invoice = PurchaseInvoice::select("purchase_invoices.*", "purchase_contracts.contract_no", "partners.name as partner_name") ->join("purchase_contracts","purchase_contracts.id","purchase_invoices.purchase_contract_id") ->join("partners","partners.id","purchase_contracts.partner_id") ->join("commodity_po","purchase_contracts.id","commodity_po.purchase_contract_id") // 手动把所有OR搜索条件包在闭包里,明确逻辑优先级 ->where(function($query) use ($searchKeyword) { $query->where("purchase_invoices.invoice_no", 'LIKE', $searchKeyword) ->orWhere("purchase_invoices.po_additional_no", 'LIKE', $searchKeyword) ->orWhere("purchase_contracts.contract_no", 'LIKE', $searchKeyword) // 给字段加表前缀,通配符放在参数里绑定 ->orWhereRaw("CONCAT(purchase_contracts.contract_no, purchase_invoices.po_additional_no) LIKE ?", [$searchKeyword]); }) // 加去重避免join导致的重复结果 ->distinct() ->get();
内容的提问来源于stack exchange,提问作者Miky Setiawan
相关产品推荐
相关产品推荐

