【问题标题】:Data type differences between PHP sqlsrv driver and PDO driverPHP sqlsrv驱动和PDO驱动的数据类型区别
【发布时间】:2020-08-04 14:38:22
【问题描述】:

这里我用的是sqlsrv:

$conn = sqlsrv_connect("192.168.1.102,1433", array("Database"=>"RF_User", "UID"=>"rfo-gcp", "PWD" => ""));

$tsql = "SELECT birthdate FROM tbl_rfaccount WHERE id = ?";

$stmt = sqlsrv_query($conn, $tsql, array("test"));

$result = sqlsrv_fetch_array($stmt);

var_dump($result);

结果:array(2) { [0]=> object(DateTime)#1 (3) { ["date"]=> string(26) "2020-04-19 20:40:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } ["birthdate"]=> object(DateTime)#1 (3) { ["date"]=> string(26) "2020-04-19 20:40:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } }

这里我用的是PDO:

$conn = new PDO("sqlsrv:Server=192.168.1.102,1433; Database=RF_User;", "rfo-gcp", "");

$tsql = "SELECT birthdate FROM tbl_rfaccount WHERE id = cast(? as varchar(13))";     

$stmt = $conn->prepare($tsql);

$stmt->execute(array("test"));

$result = $stmt->fetch(PDO::FETCH_ASSOC);

var_dump($result);

结果:array(1) { ["birthdate"]=> string(19) "2020-04-19 20:40:00" }

如果您注意到,我必须在 PDO 代码上使用 cast(? as varchar(13))。没有它不会返回任何行。在sqlsrv 上,我不必使用CAST() 函数。为什么是这样?另外,数据库上的id 列是BINARY(13),那么为什么我必须将id 转换为varchar 而不是binary(也可以使用二进制转换没有找到该行)?

【问题讨论】:

  • 您应该将其转换为绑定数组,而不是转换占位符。
  • @JayBlanchard 怎么样?这是我的理解,但可能不是你的意思:$stmt->execute(array("cast('test' as varchar(13))"));
  • ? 是一个占位符,而不是一个值,因此您将强制转换占位符是没有意义的。但是将值转换为列类型 (VARCHAR) 根本没有意义,因为该值是数字、字符串或布尔值。在这种情况下,值 text 只是一个字符串。如果您不执行此转换,您会得到什么错误? id 的列类型是什么,test 是否适合该列和类型?
  • @JayBlanchard 列类型为BINARY(13)(旧数据库,无法更改)。如果我不执行转换没有错误,它只是没有找到应该的记录。
  • 如果列类型是二进制,那么为什么要尝试从该列中选择文本值?我认为这就是断开连接的地方。

标签: php sql-server pdo binary sqlsrv


【解决方案1】:

为什么返回的日期和时间值不同?

其实这只是一个设置。

当您使用 PDO_SQLSRV(如文档中的 mentioned)时,默认返回日期和时间类型(smalldatetime、datetime、date、time、datetime2 和 datetimeoffset)作为字符串。 PDO::ATTR_STRINGIFY_FETCHES 和 PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE 属性都不起作用。为了将日期和时间类型检索为 PHP DateTime 对象,请将连接或语句属性 PDO::SQLSRV_ATTR_FETCHES_DATETIME_TYPE 设置为 true(默认为 false)。

当您使用 SQLSRV 驱动程序(同样来自 documentation)时,smalldatetime、datetime、date、time、datetime2 和 datetimeoffset 类型将作为 PHP DateTime 对象返回。可以通过在连接字符串或语句级别设置'ReturnDatesAsStrings' 选项来更改此行为。

$conn = sqlsrv_connect(
   "192.168.1.102,1433", 
    array(
       "ReturnDatesAsStrings"=>true,
       "Database"=>"RF_User", 
       "UID"=>"rfo-gcp", 
       "PWD" => ""
   )
);

请注意,某些功能取决于 PHP Driver for SQL Server 的版本。

如何转换参数值?

在语句中使用CAST()CONVERT() 函数并将参数值与字符串值绑定应该可以工作。当然,您可以在绑定参数时指定参数数据类型。

对于 PDO_SQLSRV,您应该扩展 PDOStatement::bindParam() 的语法。

对于SQLSRV,您可以在调用sqlsrv_query()\sqlsrv_execute() 时使用扩展的$params syntax 来指定SQL Server 数据类型。

我能够重现此问题(PHP 7.1.12,SQL Server 4.3.0+9904 的 PHP 驱动程序,SQL Server 2012),解决方案是使用:

$params = array($id, SQLSRV_PARAM_IN, null, SQLSRV_SQLTYPE_BINARY);          // SQLSRV
$stmt->bindParam(1, $id, PDO::PARAM_LOB, null, PDO::SQLSRV_ENCODING_BINARY); // PDO_SQLSRV 

表:

CREATE TABLE tbl_rfaccount (id binary(13), birthdate datetime)
INSERT INTO tbl_rfaccount (id, birthdate) VALUES (CONVERT(binary(13), 'Test'), GETDATE())

PHP:

<?php
...

// 
$id = "Test";

// SQLSRV
$tsql = "SELECT birthdate FROM tbl_rfaccount WHERE id = ?";
$params = array($id, SQLSRV_PARAM_IN, null, SQLSRV_SQLTYPE_BINARY);
$stmt = sqlsrv_query($conn, $tsql, $params);
if ($stmt === false) {
    echo "Error (sqlsrv_query): ".print_r(sqlsrv_errors(), true);
    exit;
}
$result = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
var_dump($result);

// PDO_SQLSRV
$tsql = "SELECT birthdate FROM tbl_rfaccount WHERE id = ?";     
$stmt = $conn->prepare($tsql);
$stmt->bindParam(1, $id, PDO::PARAM_LOB, null, PDO::SQLSRV_ENCODING_BINARY);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
var_dump($result);

...
?> 

【讨论】:

  • 我已经知道日期/时间值返回不同的原因,但感谢您澄清这一点。这里的问题是将值转换为正确的数据类型。虽然 SQLSRV 具有数据类型 SQLSRV_SQLTYPE_BINARY,但 PDO_SQLSRV 没有。我被困在使用 PDO 中,我不知道如何实现这一点。
  • @DanVeira 我认为您应该使用正确的参数绑定。请参阅更新的答案。谢谢。
  • 哇@Zhorov!很好的解释!我很少再使用 SQL Server 了,但这对我在使用 SQL Server 时遇到的一些问题有所了解。
  • @Zhorov 这行​​得通! SQLSRV(没有 PDO)在数据类型方面似乎是更好的驱动程序。另外,你熟悉 Laravel 吗?知道如何将这些驱动程序选项传递给它的查询构建器吗?谢谢!
  • @DanVeira 很高兴为您提供帮助!不,我没有使用 Laravel 的经验。
猜你喜欢
  • 2018-03-02
  • 2012-07-21
  • 1970-01-01
  • 2017-09-15
  • 1970-01-01
  • 2022-05-18
  • 2015-05-14
  • 1970-01-01
相关资源
最近更新 更多