调试 PDO mySql 将 NULL 插入数据库而不是空

2022-01-09 00:00:00 null insert php mysql pdo

我正在尝试使用 PDO 将NULL"动态插入到数据库中.

I am trying to dynamically insert 'NULL' into the database using PDO.

表结构:

CREATE TABLE IF NOT EXISTS `Fixes` (
  `Id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'PK',
  `CurrencyId` int(11) NOT NULL COMMENT 'FK',
  `MetalId` int(11) NOT NULL COMMENT 'FK',
  `FixAM` decimal(10,5) NOT NULL,
  `FixPM` decimal(10,5) DEFAULT NULL,
  `TimeStamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`Id`),
  KEY `CurrencyId` (`CurrencyId`),
  KEY `MetalId` (`MetalId`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=13 ;

PHP/PDO 查询:

PHP / PDO QUERY:

$sql = 'UPDATE 
            Fixes
    SET 
            FixAM = :fixAM,
        FixPM = :fixPM
        WHERE
            MetalId IN (SELECT Id FROM Metals WHERE Name = :metal) AND
        CurrencyId IN (SELECT Id FROM Currencies Where Id = :currency)';

$stmt = $db->prepare($sql); 

for ($i = 0; $i<3; $i++) {  
    $stmt->execute(array(
    ':metal' => 'Silver', 
    ':fixAM' => $fix['FixAM'][$i], 
    ':fixPM' => $fix['FixPM'][$i],
    ':currency' => ($i+1))
    );      
}

例如有时,$fix['FixPM'][$i] 的值sometimes 'NULL'.如何将其插入数据库?当我运行查询然后查看数据库中的数据时,这条记录显示的是 0.0000,而不是 null.

e.g. sometimes, the value for $fix['FixPM'][$i] is sometimes 'NULL'. How do I insert this into the database? When I run the query and then view the data in the database, this record shows 0.0000, and not null.

如何使用 PDO 插入 NULL 值?提供了一些解决方案.

  • 我不认为我可以使用 $stmt->execute(array( ':v1' => null, ':v2' => ... )) 作为示例,因为有时该项目为空,有时则不是.因此,我需要引用我创建的变量 $fix['FixPM'][$i] 并在需要时将其设为 null
  • I dont think I can use $stmt->execute(array( ':v1' => null, ':v2' => ... )) as per example because sometimes the item is null, and sometimes not. As such, I need to refer to the variable I have created $fix['FixPM'][$i] and make that null as and when needed

提前致谢.

推荐答案

在我看来,这似乎是 PDO 的准备好的语句模拟中的一个(n 未报告?)错误:

This appears to me to be a(n unreported?) bug in PDO's prepared statement emulation:

  1. 实现PDOStatement::execute() 最终 调用 pdo_parse_params();

反过来,试图引用/转义值 基于相关参数的数据类型(如 $data_type 参数所示).bindvalue.php" rel="nofollow">PDOStatement::bindValue()PDOStatement::bindParam()—所有参数作为 $input_parameters 提供给 PDOStatement::execute() 被视为 PDO::PARAM_STR,如该函数的文档中所述);

that, in turn, attempts to quote/escape values based on the relevant parameter's data type (as indicated by the $data_type arguments to PDOStatement::bindValue() and PDOStatement::bindParam()—all parameters provided as $input_parameters to PDOStatement::execute() are treated as PDO::PARAM_STR, as stated in the documentation of that function);

字符串类型的值被 调用相关数据库驱动的quoter() 方法,无论它们是否为 null:对于 PDO_MySQL,即为 mysql_handle_quoter(),它(最终)将值传递给 mysqlnd_cset_escape_quotes()mysql_cset_escape_slashes(),取决于在服务器的 NO_BACKSLASH_ESCAPES SQL模式;

string-typed values are escaped/quoted by calling the relevant database driver's quoter() method irrespective of whether they are null: in the case of PDO_MySQL, that's mysql_handle_quoter(), which (eventually) passes the value to either mysqlnd_cset_escape_quotes() or mysql_cset_escape_slashes(), depending on the server's NO_BACKSLASH_ESCAPES SQL mode;

给定一个 null 参数,这两个函数都返回一个空字符串.

given a null argument, both of those functions return an empty string.

我的看法是,在 开启参数的类型(在上面的步骤 2 中),如果值为 null,则 pdo_parse_params() 应将类型设置为 PDO::PARAM_NULL>.但是,有些人可能会争辩说,这将阻止在适当的情况下对 null 值进行特定类型的处理,在这种情况下,字符串大小写(在上面的第 3 步中)在继续调用驱动程序 quoter() 方法.

My opinion is that, prior to switching on the parameter's type (in step 2 above), pdo_parse_params() should set the type to PDO::PARAM_NULL if the value is null. However, some might argue that this would prevent type-specific handling of null values where appropriate, in which case the string case (in step 3 above) should definitely handle null values before proceeding with a call to the driver's quoter() method.

作为一种临时解决方法,禁用准备好的语句模拟通常是最好的:

As an interim workaround, disabling prepared statement emulation is usually for the best anyway:

$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);

相关文章