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.

Другие вопросы по тегам