C# .Net/SQL插入并选择日期时间2不匹配

C# .Net/SQL插入并选择日期时间2不匹配,c#,sql-server,.net-core,azure-sql-database,C#,Sql Server,.net Core,Azure Sql Database,我有一段代码可以在sqlAzure数据库中插入数据样本([id+datetime2]+…) 稍后,对于每个调用,我都会进行一次选择,以了解primaryKey是否已经在DB[id+datetime2]中,因此我会更新它,否则我会进行插入。 问题是,select不会返回任何内容,但是insert将得到一个重复的密钥错误。(!?) 我创建了一个代码示例来重现我的问题,不是每次都是,而是大部分 如果我替换 command.Parameters.AddWithValue("@Date", date);

我有一段代码可以在sqlAzure数据库中插入数据样本([id+datetime2]+…)

稍后,对于每个调用,我都会进行一次选择,以了解primaryKey是否已经在DB[id+datetime2]中,因此我会更新它,否则我会进行插入。 问题是,select不会返回任何内容,但是insert将得到一个重复的密钥错误。(!?)

我创建了一个代码示例来重现我的问题,不是每次都是,而是大部分

如果我替换

command.Parameters.AddWithValue("@Date", date);

它可以工作,但我想了解为什么select和insert命令不匹配

//CODE - BEGIN
//.NetCore 2.2
//SqlDatabase Azure

var date = DateTime.Now;

command.Parameters.AddWithValue("@Id", 1); 
command.Parameters.AddWithValue("@Date", Date); //{19:33:22.7727095}
command.CommandText = "INSERT INTO Answer (Id , Date) VALUES (@Id, @Date)";
command.ExecuteNonQuery();

/*DB
**IdDevice  Date
**1 2019-04-18 19:33:22.7733333
*/

//Retry
command.CommandText = "SELECT TOP 1 Id FROM Answer where Id = @Id AND Date = @Date;";
var exist = command.ExecuteScalar();
if (exist == null)
{
    throw;
}
//CODE - END
这只是一个复制行为的快速示例,我对insert+select使用了完全相同的参数,但select不会返回任何内容。
对于insert,可能.net Datetime转换为SqlDateTime,而对于Select…,在SqlDateTime2中,

AddWithValue
从提供的.net类型推断出
SqlDbType
Datetime
。然后将分数秒截断为3,并四舍五入为1/300秒,以匹配精度较低的参数数据类型。如果在没有
WHERE
子句的情况下查询数据库,您将看到这个不太精确的值

explict
SqlDbType.DateTime2
不会发生截断/舍入,因为.NET
DateTime
SqlDbType.DateTime2
都支持精度高达7的小数秒

这又是另一个理由

混合使用datetime/datetime2类型也可能导致意外行为,如
SELECT
查询所示
datetime2
的数据类型优先级高于datetime,因此使用扩展到更高精度的
datetime
实际1/300秒值(而不是四舍五入/截断值)比较分数秒值。考虑一下这些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()

您的Id列中的数据在db中插入-1,并在右侧选择“不工作”!它是否位于db store-1中的列Id中?command.Parameters.AddWithValue(“@Date”,value.Date)//{19:33:22.7727095}在这行中什么是值?不要使用
AddWithValue
。创建一个具有特定类型和精度的参数,并设置其值。@hassan.ef my bad,其“1”以db表示,如果在日期本地化,则会出现问题;)@AmitYadav我的错误再次出现,在实际代码和示例代码之间存在错误,值是DateTime.now是肯定的,正如我所说的,如果我使用command.Parameters.Add(“@Date”,SqlDbType.DateTime2)。值=日期;它工作得很好,但我不明白使用相同的Sql.Parameter(添加AddParameterWithValue)如何可以给出在选择和插入命令中使用的不同结果…@CarlosBarroso,我将添加到我的答案中,以解释为什么使用相同的参数值会出现意外结果。我完全理解您的示例,但这不是我真正的用例。恢复
command.Parameters.AddWithValue(“@Date”,Datetime.Now);command.CommandText=“插入应答(日期)值(@Date)”;command.ExecuteNonQuery();command.CommandText=“从答案中选择前1个Id,其中日期=@Date;”;command.ExecuteScalar()//返回null
相同的数据库、相同的表、相同的类型、相同的参数、select和insert之间的不同行为。@CarlosBarroso,您的案例与T-SQL示例完全相同,但使用了
WHERE
子句而不是
IF
语句。我添加了一个.NET示例来说明比较行为是相同的。您提到了相同的类型,但类型不同(SqlDbType.DateTime而不是SqlDbType.DateTime2)。在TSQL中,您将sqlDatetime与sqlDatetime2进行比较,因此预期会失败,与我的行为没有任何共同之处。但是您的.net示例完美地再现了这种行为,我仍然不理解为什么insert将使用SqlDateTime2,而Where将使用舍入的SqlDateTime,这毫无意义:)
$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()