PHP使用PDO执行MariaDB动态透视SQL报1064语法错误求解决
问题原因
- PDO默认禁止单次执行多条SQL语句,你把
SET、SELECT、PREPARE、EXECUTE等多个SQL语句拼接后一次性传入prepare()执行,直接触发语法报错。 - PHP代码中SQL字符串拼接存在语法错误:你给第一句
$sql1 = "SET @sql = NULL;";末尾加了赋值结束的分号,下一行直接以.开头拼接字符串属于PHP语法错误,最终拼接出来的SQL内容本身就是残缺的。 - MySQL默认
GROUP_CONCAT函数长度限制为1024字节,如果你的meta_key数量较多,拼接出来的动态SQL会被截断,也会导致语法错误。
修复方案
方案1(更稳妥,完全避免MySQL动态SQL风险)
直接在PHP侧拼接透视SQL,不需要依赖MySQL用户变量:
global $con; $hasil = array(); try { // 第一步:先查询所有需要透视的meta_key $stmt = $con->query("SELECT DISTINCT meta_key FROM wp_frmt_form_entry_meta RIGHT JOIN wp_frmt_form_id ON wp_frmt_form_entry_meta.meta_key = wp_frmt_form_id.meta_keyy ORDER BY wp_frmt_form_entry_meta.meta_id"); $metaKeys = $stmt->fetchAll(PDO::FETCH_COLUMN); // 第二步:在PHP侧拼接透视字段部分 $selectFields = ["`entry_id`"]; foreach ($metaKeys as $key) { // 转义key避免SQL注入 $escapedKey = $con->quote($key); $selectFields[] = "MAX(IF(`meta_key` = $escapedKey, `meta_value`, '')) AS `" . str_replace("`", "``", $key) . "`"; } // 第三步:拼接完整的查询SQL执行 $sql = "SELECT " . implode(',', $selectFields) . " FROM wp_frmt_form_entry_meta s GROUP BY s.`entry_id` ORDER BY s.`entry_id`"; $stmt = $con->query($sql); $rs = $stmt->fetchAll(PDO::FETCH_ASSOC); if($rs){ foreach ($rs as $i => $val) { $hasil[$i]['name-1'] = $val['name-1'] ?? ''; // 其余字段按需求处理 } } } catch (Exception $e) { echo 'Error getAllData = '.$e->getMessage(); } return $hasil;
方案2(保留原SQL逻辑,修复执行方式)
如果要保留原MySQL动态SQL逻辑,需要调整配置和代码:
- 初始化PDO的时候开启模拟预处理,支持多语句执行:
$con = new PDO("mysql:host=你的数据库地址;dbname=库名;charset=utf8mb4", '账号', '密码', [ PDO::ATTR_EMULATE_PREPARES => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]);
- 修复SQL字符串拼接错误,去掉第一行赋值后的多余分号:
$sql1 = "SET @sql = NULL;" ."SELECT GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(`meta_key` = \"', `meta_key`,'\", `meta_value`,\"\")) AS \"',`meta_key`,'\"')) INTO @sql FROM wp_frmt_form_entry_meta RIGHT JOIN wp_frmt_form_id ON wp_frmt_form_entry_meta.meta_key = wp_frmt_form_id.meta_keyy ORDER BY wp_frmt_form_entry_meta.meta_id; SET @sql = CONCAT('SELECT `entry_id`, ', @sql, ' FROM wp_frmt_form_entry_meta s GROUP BY s.entry_id ORDER BY s.entry_id'); PREPARE a FROM @sql; EXECUTE a;";
- 提前修改MySQL
GROUP_CONCAT长度限制,避免SQL截断:在执行动态SQL前先执行$con->query("SET SESSION group_concat_max_len = 102400;");
注意事项
- 多语句执行存在SQL注入风险,优先选择方案1在PHP侧拼接SQL,可控性更高。
- 所有动态拼接到SQL中的字段值都要做好转义处理,避免注入风险。
内容的提问来源于stack exchange,提问作者Paulus Andry
相关产品推荐
相关产品推荐

