ADO.NET и SSMS интерпретируют параметры SP по-разному, или SQL-профилировщик имеет ошибку?
Постарайся быть кратким. Вот моя таблица и SP в базе данных SQL Server 2008 R2:
CREATE TABLE dbo.TEST_TABLE
(
ID INT NOT NULL IDENTITY (1,1) PRIMARY KEY,
VALUE varchar(23)
)
GO
CREATE PROCEDURE USP_TEST_PROCEDURE
(
@Param1 varchar(23)
)
AS
BEGIN
INSERT INTO TEST_TABLE (VALUE) VALUES (@Param1)
END
GO
Вот код C#:
using (SqlConnection con = GetConection())
{
using (var cmd = new SqlCommand("USP_TEST_PROCEDURE", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Param1", SqlDbType.DateTime, 23).Value = "2013-12-10 12:34:56.789";
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
}
Обратите внимание, что я намеренно указал тип @Param1 как DateTime. Теперь запустите SQL Server Profiler и запустите код.NET. Когда закончите, не останавливайте профилировщик, просто посмотрите на вывод. Я вижу следующий текст для RPC: Завершенный EventClass:
exec USP_TEST_PROCEDURE @Param1='2013-12-10 12:34:56.790'
Теперь скопируйте этот текст, откройте SSMS, вставьте в новое окно запроса и запустите его без изменений. Теперь остановите профилировщик и убедитесь, что SSMS выдал тот же текст в профилировщике, что и приложение ADO.NET:
exec USP_TEST_PROCEDURE @Param1='2013-12-10 12:34:56.790'
Теперь выберите из таблицы TEST_TABLE. Я вижу следующий результат:
ID ЗНАЧЕНИЕ 1 декабря 10 2013 12:34 2 2013-12-10 12:34:56.790
Вопрос в том, почему две одинаковые команды (с точки зрения SQL Server Profiler) дают разные результаты?
Кажется, что профилировщик не показывает правильно, что происходит за сценой. После запуска приложения.NET я ожидал увидеть следующее:
DECLARE @Param1 Datetime = '2013-12-10 12:34:56.789';
exec USP_TEST_PROCEDURE @Param1=@Param1
В этом случае было бы понятно, почему у меня 790 миллисекунд вместо 789. Это потому, что тип datetime не очень точный. Он округляется с шагом.000,.003 или.007 секунд. См.: datetime (Transact-SQL). Было бы также понятно, почему у меня вместо '2013-12-10 12: 34: 56.790' вместо '2013-12-10 12: 34: 56.790' указано "10 декабря 2013 12:34". Это связано с тем, что формат "mon dd гггг чч:miAM (или PM)" используется по умолчанию при преобразовании даты и времени в строку. См. CAST и CONVERT (Transact-SQL). Следующий пример демонстрирует, что:
DECLARE @Param1 Datetime = '2013-12-10 12:34:56.789';
DECLARE @Param1_varchar varchar(23);
SELECT @Param1 --2013-12-10 12:34:56.790
SET @Param1_varchar = @Param1;
SELECT @Param1_varchar; --Dec 10 2013 12:34PM
GO
Если мое понимание верно, то я хотел бы перефразировать вопрос: почему SQL Server Profiler показывает что-то странное и запутанное? Есть ли варианты, чтобы исправить это?
1 ответ
Ваше приложение отправляет дату и время, но поскольку вы выбрали столбец varchar, он неявно преобразуется при входе. Как и в случае с print:
DECLARE @d DATETIME = GETDATE();
SELECT @d; -- this is still a datetime
PRINT @d; -- this is implicitly converted to a string
Если вам не нравится это запутанное поведение, тогда прекратите смешивать типы данных. Нет абсолютно никаких причин хранить значение datetime в столбце varchar.