如何将 ISO8601 TSQL DATETIME 参数与 PDO 绑定?

Posted

技术标签:

【中文标题】如何将 ISO8601 TSQL DATETIME 参数与 PDO 绑定?【英文标题】:How to bind ISO8601 TSQL DATETIME parameter with PDO? 【发布时间】:2016-12-26 21:49:41 【问题描述】:

看来 PDO 的ISO 8601 格式化时间戳有问题。

我使用Microsoft® ODBC Driver 13 (Preview) for SQL Server®从运行 php 7.0.8 的 64 位 Ubuntu 16.04 连接

这是我的简单表格:

CREATE TABLE dtest (
    "stamp" DATETIME
);

作品:

$pdoDB = new PDO('odbc:Driver=ODBC Driver 13 for SQL Server;
  Server='.DATABASE_SERVER.';
  Database='.DATABASE_NAME,
  DATABASE_USERNAME,
  DATABASE_PASSWORD
);
$pdoDB->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

$sql = "INSERT INTO dtest (stamp) VALUES ('2011-03-15T10:23:01')";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);

不起作用:

$sql = "INSERT INTO dtest (stamp) VALUES (?)";
$stmt = $pdoDB->prepare($sql);
$params = ['2011-03-15T10:23:01'];
$stmt->execute($params);

致命错误:未捕获的 PDOException:SQLSTATE[22018]:转换规范的字符值无效:0 [Microsoft][SQL Server 的 ODBC 驱动程序 13]转换规范的字符值无效(SQLExecute[0] at /build/php7. 0-lPMnpS/php7.0-7.0.8/ext/pdo_odbc/odbc_stmt.c:260)

这行得通如果我删除T 所以'2011-03-15T10:23:01' 变成'2011-03-15 10:23:01'

$sql = "INSERT INTO dtest (stamp) VALUES (?)";
$stmt = $pdoDB->prepare($sql);
$params = ['2011-03-15 10:23:01'];
$stmt->execute($params);

但我正在编写一个每晚在大约 200 万条记录上运行的脚本,所以我真的宁愿不承担运行数百万条 str_replace('T', ' ', $param)

的开销

我也尝试过使用bindParam,但它给出了同样的错误

$sql  = "INSERT INTO dtest (stamp) VALUES (:tdate)";
$stmt = $pdoDB->prepare($sql);
$date = '2011-03-15T10:23:01';
$stmt->bindParam(':tdate',$date,PDO::PARAM_STR);
$stmt->execute();

有没有按原样绑定和执行这个参数?我对错误消息有点怀疑,因为它似乎来自 SQL Server,好像 PDO 做得很好,但这没有意义,因为 它能够在没有参数化的情况下处理类型转换.


我也试过SQL转换:

作品:

$sql = "INSERT INTO dtest (stamp) VALUES (CONVERT(DATETIME, '2011-03-15T10:23:02', 126))";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);

不起作用:

$sql = "INSERT INTO dtest (stamp) VALUES (CONVERT(DATETIME, ?, 126))";
$stmt = $pdoDB->prepare($sql);
$params = ['2011-03-15T10:23:02'];
$stmt->execute($params);

【问题讨论】:

你好,你好运吗? @MonkeyZeus 不,但我整个周末都休息了,所以今天我要再试一次。 祝你好运!告诉我进展如何 @JeffPuckettII 你能解决这个问题吗?这不仅发生在日期上,还发生在所有绑定参数上。 @Frondor 对于这个特定的问题,我正在导入安全(预清理)数据,所以我完全跳过了绑定。不是用户输入的解决方案,所以不,我还没有解决它。我只是尽量避免微软支持不佳的垃圾。 【参考方案1】:

您需要使用 SQL Server 的内置 convert() 函数并指定您提供的格式 (126):

$sql = "INSERT INTO dtest (stamp) VALUES (CONVERT(DATETIME, '2011-03-15T10:23:01', 126))";

文档在字符串末尾提到 :mmm,因此您可能需要在日期字符串末尾手动添加 :000 才能使其正常工作。

【讨论】:

感谢您的回答,但我在尝试$sql = "INSERT INTO dtest (stamp) VALUES (convert(?, 126))"; 时遇到同样的错误另请注意,未经准备的语句无需转换即可正常工作。 $sql = "INSERT INTO dtest (stamp) VALUES ('2011-03-15T10:23:01')"; 是绑定过程导致问题。 其实这会导致额外的Syntax error or access violation: 102 Incorrect syntax near '@P1'. @JeffPuckettII 哎呀!很抱歉,我应该更仔细地阅读您的问题详细信息。不过这很奇怪。您是否考虑过将bindParam()PARAM_STR 一起使用,看看情况是否会好转? 不,这给出了同样的错误 - 我已经通过尝试更新了我的问题。 @JeffPuckettII 我完全没有想法,mssql 驱动程序可能只是有一个未解决的错误:(【参考方案2】:

花了半天时间试图解决同样的问题后,我最终放弃了 odbc 并改用了 dblib。我安装了 php7.0-sybase 包,修改了我的 PDO 连接的数据源名称并一劳永逸地解决了。 现在每个绑定都在工作。

【讨论】:

从技术上讲,这不是特定问题的答案,但它是一个非常好的解决方法,我非常想“接受”它。你是对的,我非常喜欢 Sybase 驱动程序,以至于我已经很久没有使用过 ODBC。但我知道我将来会有更多需要 ODBC 的项目(例如 DB2),希望有人能特别回答这个问题。

以上是关于如何将 ISO8601 TSQL DATETIME 参数与 PDO 绑定?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ISO 8601 格式的 DateTime 字段将 JSON 文本反序列化为 BsonDocument?

如何从 ISO 8601 格式创建 .NET DateTime

如何解析 ISO 8601 格式的日期?

如何在 Pandas 数据框中将日期转换为 ISO-8601 DateTime 格式

将 Luxon 日期格式化为 ISO8601 基本格式

使用具有 ISO 8601 格式的“DateTime.LocalNow”