为何PDO在静态与动态值联合查询中返回整数为字符串?
PDO联合查询返回字段类型异常问题
环境与测试配置
PDO配置
$options = [ \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, \PDO::ATTR_EMULATE_PREPARES => false, ];
user表结构
CREATE TABLE `user` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `admin` tinyint(1) unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `id_UNIQUE` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
测试情况
- 普通查询:返回
admin为整数,符合预期
$sql = "SELECT admin FROM user"; $pdo_statement = $pdo->query($sql); $result = $pdo_statement->fetchAll(); var_dump($result); // > array(1) { [0]=> array(1) { ["admin"]=> int(1) } } ...
- 静态值联合查询:返回
admin为整数,符合预期
$sql = "SELECT 0 AS admin FROM user UNION SELECT 1 AS admin FROM user"; $pdo_statement = $pdo->query($sql); $result = $pdo_statement->fetchAll(); var_dump($result); // > array(1) { [0]=> array(1) { ["admin"]=> int(1) } } ...
- 静态与动态值混合联合查询:返回
admin为字符串,不符合预期
$sql = "SELECT admin FROM user UNION SELECT 1 AS admin FROM user"; $pdo_statement = $pdo->query($sql); $result = $pdo_statement->fetchAll(); var_dump($result); // > array(1) { [0]=> array(1) { ["admin"]=> string(1) "1" } } ...
测试环境
Apache 2.4.51 PHP 5.6.40 | 7.4.26 | 8.0.13 | 8.1.0 MySQL 8.0.27 MariaDB 10.6.5
疑问
为何第三种混合联合查询中,PDO会将admin字段以字符串类型返回而非整数?这是否为PDO的Bug,还是存在我忽略的细节?
解答:这不是PDO的Bug,是MySQL类型规则与PDO驱动逻辑共同作用的结果
1. MySQL的UNION类型统一规则
当使用UNION合并多个查询结果时,MySQL会为结果集的每个字段确定一个兼容所有子查询的统一类型:
- 第三种查询中,第一个子查询的
admin是tinyint(1) unsigned(无符号小整数),第二个子查询的1是默认的int(11)(有符号整数)。 - 由于两个类型分属不同符号属性的数值类型,MySQL会将结果集的字段类型转换为**
DECIMAL类型**——这是一种高精度数值类型,能同时兼容无符号和有符号数值的范围。
2. PDO对DECIMAL类型的处理逻辑
PDO的MySQL驱动在处理DECIMAL类型字段时,默认返回字符串而非整数。原因是DECIMAL可以存储超出PHP整数范围的高精度数值,直接转换为整数可能导致精度丢失或溢出,因此驱动选择以字符串形式保留完整数值。
3. 前两种情况正常的原因
- 第一种情况:直接查询表字段,返回的是
tinyint类型,PDO驱动可安全转换为PHP整数。 - 第二种情况:两个子查询都是字面量整数,MySQL会将结果集类型统一为
int,PDO驱动直接转换为PHP整数。
解决方法
如果需要确保返回整数类型,可通过以下方式处理:
- SQL层面显式转换:将字面量转换为与表字段一致的类型,比如
SELECT CAST(1 AS UNSIGNED TINYINT) AS admin,这样UNION后的字段类型保持为tinyint unsigned,PDO会返回整数。 - PHP层面手动转换:获取结果后,将字符串字段值强制转换为整数,例如
$result[0]['admin'] = (int)$result[0]['admin'];。
内容的提问来源于stack exchange,提问作者Xintaur
相关产品推荐
相关产品推荐

