You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP正则拆分街道数据插入location表:空值与批量ID匹配问题求助

问题排查与解决方案

咱们一步步拆解并解决你遇到的问题:

1. 核心问题:循环内全表插入导致主键冲突

你当前代码在while循环里执行INSERT INTO location SELECT id, ... FROM branch,这意味着每次循环都会把整个branch表的数据插入一次。第一次循环执行后,location表已经存入了所有branch的id记录,后续循环再执行时,因为location.id是主键,会触发主键重复错误,所以看起来只有第一条结果被插入(实际是第一次循环插入了所有数据,后面的都报错终止了)。

2. 正则表达式优化

你的正则可能会匹配到街道名末尾的多余空格,比如示例中的"Krakowska "(带空格),可以调整为更精准的正则,确保拆分出干净的街道名和门牌号:

preg_match('/^([^\d]+?)\s*(\d.*)?$/', $row['street'], $matches);
  • ^([^\d]+?):非贪婪匹配开头的非数字部分(街道名),避免末尾多余空格
  • \s*:匹配街道名和门牌号之间的任意空格
  • (\d.*)?:可选的门牌号部分,以数字开头,后面可以跟任意字符(比如A/B)

3. 空值处理:插入SQL的NULL而非字符串'NULL'

当正则没有匹配到街道名或门牌号时,对应的数组元素不存在,这时候需要将变量设为NULL,并且在SQL语句中直接写入NULL(不要加单引号),否则会插入字符串'NULL',而不是数据库的空值。

4. 关联branch.id与location.id的正确方式

应该在循环中每次处理branch的单条记录,获取对应的id,然后针对location表的该id执行插入或更新操作。


修改后的完整代码

<?php 
$db = mysqli_connect('localhost','root','','jakoscobslugi') or die('Error connecting to MySQL server.'); 

// 清空location表
$query0 = "TRUNCATE location"; 
mysqli_query($db,$query0) or die('Error query0.'); 

// 查询branch表的id、street、phone(需要关联id和phone字段)
$result = mysqli_query($db, "SELECT id, street, phone FROM branch;"); 

while($row = mysqli_fetch_assoc($result)) { 
    // 初始化变量为NULL
    $streetName = NULL;
    $streetNumber = NULL;
    
    // 执行正则拆分
    if (preg_match('/^([^\d]+?)\s*(\d.*)?$/', $row['street'], $matches)) {
        // 处理街道名:去除首尾空格
        if (!empty($matches[1])) {
            $streetName = trim($matches[1]);
        }
        // 处理门牌号:如果匹配到就赋值
        if (isset($matches[2])) {
            $streetNumber = $matches[2];
        }
    }

    // 打印调试信息
    echo "[". ($streetName ?? 'NULL') ."] [". ($streetNumber ?? 'NULL') ."]<br />"; 

    // 准备SQL语句:处理NULL值,避免单引号包裹
    $streetSql = $streetName === NULL ? 'NULL' : "'". mysqli_real_escape_string($db, $streetName) ."'";
    $numberSql = $streetNumber === NULL ? 'NULL' : "'". mysqli_real_escape_string($db, $streetNumber) ."'";

    // 针对当前branch的id执行插入/更新
    $query2 = "INSERT INTO location(id, street, house_number, phone_number) 
               VALUES({$row['id']}, {$streetSql}, {$numberSql}, '". mysqli_real_escape_string($db, $row['phone']) ."')
               ON DUPLICATE KEY UPDATE 
                   street = {$streetSql}, 
                   house_number = {$numberSql}, 
                   phone_number = '". mysqli_real_escape_string($db, $row['phone']) ."'";

    mysqli_query($db, $query2) or die('Error query2: ' . mysqli_error($db)); 
} 

// 关闭数据库连接
mysqli_close($db);
?>

代码关键点说明

  • SQL注入防护:使用mysqli_real_escape_string()转义字符串变量,避免SQL注入风险
  • 空值处理:通过三元判断,将PHP的NULL转换为SQL的NULL,而非字符串形式
  • 单条记录处理:每次循环只处理branch的一条记录,通过id精准关联location的对应行
  • 正则优化:确保街道名无多余空格,门牌号匹配更准确

额外建议:用MySQL原生正则函数实现(更高效)

如果数据量较大,用MySQL原生的正则函数直接在SQL中处理会更高效,不需要PHP循环处理,示例SQL如下:

TRUNCATE location;

INSERT INTO location(id, street, house_number, phone_number)
SELECT 
    id,
    -- 提取街道名:去掉末尾空格和门牌号部分
    TRIM(REGEXP_REPLACE(street, '\\s*\\d.*$', '')) AS street,
    -- 提取门牌号:提取数字开头的部分
    REGEXP_SUBSTR(street, '\\d.*$') AS house_number,
    phone
FROM branch
ON DUPLICATE KEY UPDATE
    street = TRIM(REGEXP_REPLACE(branch.street, '\\s*\\d.*$', '')),
    house_number = REGEXP_SUBSTR(branch.street, '\\d.*$'),
    phone_number = branch.phone;

这种方式不需要PHP循环,直接通过SQL完成所有操作,性能更好,也避免了PHP和数据库之间的多次交互。

内容的提问来源于stack exchange,提问作者pelkas13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:37:58