AddWithValue
выводит SqlDbType
из поставляемого типа .NET как DateTime
, а не DateTime2
.Дробные секунды затем усекаются с точностью до 3 и округляются до 1/300 секунды, чтобы соответствовать менее точному типу данных параметра.Это менее точное значение вы увидите, если запросите базу данных без предложения WHERE
.
С экспликацией SqlDbType.DateTime2
не происходит усечения / округления, поскольку .NET DateTime
и SqlDbType.DateTime2
оба поддерживают доли секундыс точностью до 7.
Это еще одна причина, чтобы избегать AddWithValue
.
Смешивание типов datetime / datetime2 также может привести к неожиданному поведению, как показаноВаш SELECT
запрос.datetime2
имеет более высокий приоритет типа данных, чем datetime, поэтому значение доли секунды сравнивается с использованием фактического значения 1/300 секунды datetime
, расширенного с большей точностью, а не округленного / усеченного.Рассмотрим эти примеры T-SQL:
--these values compare not equal because the datetime value of 1/300 is actually .003333333333...
DECLARE @datetime datetime = '2019-04-19T00:00:00.003';
DECLARE @datetime2 datetime2 = '2019-04-19T00:00:00.003';
IF @datetime = @datetime2 PRINT 'EQUAL' ELSE PRINT 'NOT EQUAL';
GO
--these values compare not equal because the datetime value is actually .006666666666...
DECLARE @datetime datetime = '2019-04-19T00:00:00.007';
DECLARE @datetime2 datetime2 = '2019-04-19T00:00:00.007';
IF @datetime = @datetime2 PRINT 'EQUAL' ELSE PRINT 'NOT EQUAL';
GO
--these values comare equal because the datetime value is .010000000000...
DECLARE @datetime datetime = '2019-04-19T00:00:00.010';
DECLARE @datetime2 datetime2 = '2019-04-19T00:00:00.010';
IF @datetime = @datetime2 PRINT 'EQUAL' ELSE PRINT 'NOT EQUAL';
GO
Хотя этим поведением сравнения критическое изменение можно управлять, запустив уровень совместимости базы данных 120 или ниже, это будетЛучше всего просто соответствовать типам SQL.Это обеспечит наилучшую производительность и защиту вашего кода в будущем.
РЕДАКТИРОВАТЬ:
Такое же поведение может быть продемонстрировано с параметрами .NET несовпадающих типов.Ниже приведен пример PowerShell.
$connectionString = "Data Source=.;Initial Catalog=tempdb;Integrated Security=SSPI"
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
$connection.Open()
$command = New-Object System.Data.SqlClient.SqlCommand("CREATE TABLE dbo.Answer (Id int NOT NULL, Date datetime2 NOT NULL);", $connection)
[void]$command.ExecuteNonQuery()
$command.CommandText = "INSERT INTO dbo.Answer (Id, Date) VALUES (@Id, @Date);"
[void]$command.Parameters.AddWithValue("@Id", 1)
[void]$command.Parameters.AddWithValue("@Date", [DateTime]::Parse("2019-04-19T00:00:00.003"))
[void]$command.ExecuteNonQuery()
$command.CommandText = "SELECT TOP 1 Id FROM Answer where Id = @Id AND Date = @Date;"
$exists = $command.ExecuteScalar()
# not exists
if($exists -ne $null) { Write-Host "exists" } else { Write-Host "not exists" }
$command.CommandText = "INSERT INTO dbo.Answer (Id, Date) VALUES (@Id, @Date);"
$command.Parameters["@Id"].Value = 2
$command.Parameters["@Date"].Value = [DateTime]::Parse("2019-04-19T00:00:00.007")
[void]$command.ExecuteNonQuery()
$command.CommandText = "SELECT TOP 1 Id FROM Answer where Id = @Id AND Date = @Date;"
$exists = $command.ExecuteScalar()
# not exists
if($exists -ne $null) { Write-Host "exists" } else { Write-Host "not exists" }
$command.CommandText = "INSERT INTO dbo.Answer (Id, Date) VALUES (@Id, @Date);"
$command.Parameters["@Id"].Value = 3
$command.Parameters["@Date"].Value = [DateTime]::Parse("2019-04-19T00:00:00.010")
[void]$command.ExecuteNonQuery()
$command.CommandText = "SELECT TOP 1 Id FROM Answer where Id = @Id AND Date = @Date;"
$exists = $command.ExecuteScalar()
# exists
if($exists -ne $null) { Write-Host "exists" } else { Write-Host "not exists" }
$connection.Close()