如何合并同库car、bike表SELECT查询 避免重复条目与多次DB连接
同库两张表SELECT查询合并方案
直接使用UNION ALL实现查询合并,避免JOIN产生的笛卡尔积重复条目,同时减少数据库请求次数,具体实现如下:
核心思路
- 两张表字段不完全一致(car表含
city字段、bike表含village字段),不适合用JOIN关联,UNION ALL会把两个查询的结果集纵向拼接,不会产生交叉关联导致的重复行 - 把原PHP循环中的过滤条件直接下推到SQL层,无需查询全量数据后再在PHP中做判断,性能更高
- 合并查询时明确指定返回字段,缺失字段用NULL占位,保证两个SELECT子句返回的列数、字段顺序、数据类型完全一致,额外增加来源标记字段方便后续业务区分数据来源
合并后的SQL语句
SELECT id, distance, airport, country, city, NULL AS village, 'car' AS source_table FROM car WHERE price <> '' AND ( (city = ? AND airport = ?) OR (airport = ? AND country = ?) ) UNION ALL SELECT id, distance, airport, country, NULL AS city, village, 'bike' AS source_table FROM bike WHERE price <> '' AND ( (village = ? AND airport = ?) OR (airport = ? AND country = ?) ) ORDER BY id ASC
说明:使用
UNION ALL而非UNION,因为UNION会对全结果集做去重排序,产生额外性能开销,两个表的结果自带来源标记不存在重复可能,UNION ALL执行效率更高。语句中用?作为参数占位符,配合预处理语句可避免SQL注入风险。
对应PHP实现代码
替换原有的两次查询、两次循环逻辑为单次查询处理:
// 初始化预处理语句 $stmt = mysqli_prepare($conn, " SELECT id, distance, airport, country, city, NULL AS village, 'car' AS source_table FROM car WHERE price <> '' AND ( (city = ? AND airport = ?) OR (airport = ? AND country = ?) ) UNION ALL SELECT id, distance, airport, country, NULL AS city, village, 'bike' AS source_table FROM bike WHERE price <> '' AND ( (village = ? AND airport = ?) OR (airport = ? AND country = ?) ) ORDER BY id ASC "); // 绑定入参,参数顺序与SQL中?占位符一一对应,所有参数为字符串类型所以用s标记 mysqli_stmt_bind_param($stmt, "ssssssss", $to, $from, $to, $from, $to, $from, $to, $from); // 执行查询获取结果集 mysqli_stmt_execute($stmt); $qry = mysqli_stmt_get_result($stmt); $distance = null; // 单次循环处理所有结果,无重复条目 while ($val = mysqli_fetch_array($qry)) { // 如需区分数据来源,可通过$val['source_table']判断值为car/bike $distance = $val['distance']; // 原有业务逻辑直接写在这里即可 }
常见避坑点
- 禁止无关联条件直接JOIN两张表:会生成两张表行数乘积的笛卡尔积,产生大量无意义重复数据
- 禁止在UNION查询中使用
SELECT *:两张表字段不一致时会直接报错,必须明确指定返回字段,缺失字段用NULL补位对齐 - 不要把全量数据拉到PHP层再过滤:数据库层面做条件筛选的效率远高于PHP循环,同时能减少数据库和PHP之间的网络传输开销
内容的提问来源于stack exchange,提问作者evavienna
相关产品推荐
相关产品推荐

