Как связать параметр TSQL DATETIME ISO8601 с PDO?

Кажется, что PDO имеет проблемы с ISO 8601 отформатированными временными метками.

Я подключаюсь из 64-разрядного Ubuntu 16.04, работающего под управлением PHP 7.0.8, используя Microsoft® ODBC Driver 13 (Preview) для SQL Server®

Вот моя простая таблица:

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);

Неустранимая ошибка: Uncaught PDOException: SQLSTATE [22018]: Недопустимое значение символа для спецификации литья: 0 [Microsoft] [драйвер ODBC 13 для SQL Server] Недопустимое значение символа для спецификации литья (SQLExecute [0] at/build/php7. 0-lPMnpS/php7.0-7.0.8/внутр/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);

Но я пишу script, который работает в ночное время примерно на 2 миллиона записей, поэтому я бы действительно не переносил накладные расходы на запуск миллионов 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);

Ответ 1

Вам понадобится встроенная функция convert() SQL Server и укажите формат (126) которую вы ему даете:

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

В документации упоминается :mmm в конце строки, поэтому вам может потребоваться вручную добавить :000 в конец строки даты, чтобы это работало.

Ответ 2

После того, как полдня, потратив попытку решить ту же проблему, я закончил тем, что сбросил odbc и вместо этого использовал dblib. Я установил пакет php7.0-sybase, адаптировал имя источника данных моего PDO-соединения и разрешил один раз для всех. Теперь каждая привязка работает.