我不确定我的问题标题是否完美 - 所以请允许我进一步解释。
以下是一些测试数据的快照:
这是我的代码:
Function TestDb() As ActionResult
Dim clientLocId As Integer = 23
Dim showDate As New Date
showDate = New Date(2015, 8, 14)
'showDate = New Date(2015, 9, 22)
'showDate = New Date(2015, 9, 27)
Dim orderRecs = db.Orders.Where(Function(x) x.ClientLocationId = clientLocId AndAlso x.OrderNumber IsNot Nothing _
AndAlso x.DateCompletedUtc IsNot Nothing _
AndAlso DbFunctions.TruncateTime(x.OrderDateLoc) = showDate.Date) _
.OrderByDescending(Function(x) x.OrderDateUtc)
Stop
End Function
以下是我的问题:
订单日期09/27/2015和09/22/2015的行使用上面的逻辑正确查询 - 每个请求的日期产生1行。但是 - 对2015年8月14日日期的查询产生没有。如果重要的话,我现在在-04:00时区。如果我将行数据
我用谷歌搜索试图找到答案,但已经干了。有人可以权衡我的问题吗?
[更新]:解决方法 以下是基于this thread @PiotrAuguscik建议首先将查询转换为列表的建议(目前):
Dim orderRecs = (db.Orders.Where(Function(x) x.ClientLocationId = clientLocId AndAlso x.OrderNumber IsNot Nothing _
AndAlso x.DateCompletedUtc IsNot Nothing).ToList) _
.Where(Function(x) x.OrderDateLoc.Value.Date = showDate.Date) _
.OrderByDescending(Function(x) x.OrderDateUtc)
它有点"硬壳",但它有效。不过,我当然想知道为什么时区与DbFunctions.TruncateTime()有关。
[更新#2]来自Matt Johnson答案的正确解决方案代码
Dim orderRecs = db.Orders.Where(Function(x) x.ClientLocationId = clientLocId AndAlso x.OrderNumber IsNot Nothing _
AndAlso x.DateCompletedUtc IsNot Nothing AndAlso
(x.OrderDateLoc >= showDateDto AndAlso x.OrderDateLoc < showDateDto.AddDays(1))) _
.OrderByDescending(Function(x) x.OrderDateUtc)
答案 0 :(得分:2)
一些事情:
您的原始查询和解决方法均为non-sargable。你永远不应该在WHERE
子句中操纵比较的左侧。如果这样做,数据库将无法使用任何索引,并且您拥有的数据越多,速度越慢越慢。相反,请进行范围查询。
您的表格中似乎有datetimeoffset
种类型。这些代表特定的时刻,因此根据它们的UTC等价物而不是它们的本地显示时间来比较两个datetimeoffset
值。值也以这种方式编制索引。
并非所有人都同时观察相同的日历日期。你需要问问自己,“我要求约会对象?”
如果是进行查询的人的日期,那么您的输入值应该反映出来。而不是根据本地时间将VB Date
(System.DateTime
)传入您的查询,而是传入基于UTC的DateTime
或DateTimeOffset
。请记住,您需要进行范围查询,因此您将计算它们的对,作为半开区间。换句话说:
// this example uses the local time zone, but there are other ways also.
DateTimeOffset startDto = new DateTimeOffset(showDate.Date)
DateTimeOffset endDto = new DateTimeOffset(showDate.Date.AddDays(1))
// then in the query...
... x.OrderDateLoc >= startDto && x.OrderDateLoc < endDto
如果您希望在记录时匹配本地日期,那么您还需要在SQL Server数据库中执行其他工作。
首先,您需要删除convert(datetime2, yourDateTimeOffset)
的偏移量,或者只计算convert(date, yourDateTimeOffset)
的原始本地日期。您应该在computed column中执行此操作,以便您也可以在其上创建索引。
然后,您可以使用该计算列进行范围查询,或者如果您计算到日期,那么您只需对其进行相等比较。
一般情况下,我会避免在where子句中使用DbFunctions.TruncateTime
。当用于datetimeoffset
字段时,它会转换为效率相当低的SQL,如下所示:
convert(datetimeoffset, convert(varchar(255), yourField, 102) + ' 00:00:00 ' + Right(convert(varchar(255), yourField, 121), 6), 102)
基本上,这会使用字符串重新构建datetimeoffset
,同时保留偏移但将时间设置为午夜,这可能不是您真正想要做的。您可以在SQL事件探查器中自己看到这一点。
答案 1 :(得分:0)
这是对马特·约翰逊(Matt Johnson)的回应。上面的查询不一定是不可保留的,它取决于索引。当您使用索引作为字段函数参数时,它变得不可保留。 :)