2015-10-05 44 views
2

我不確定我的問題標題是完美的 - 所以請允許我進一步解釋一下。DbFunctions.TruncateTime在不同服務器時區的行爲有所不同嗎?

下面是一些測試數據的快照:

enter image description here

這裏是我的代碼:

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 

所以這裏是我的問題:

訂單日期2015年9月27日和2015年9月22日的行使用上述邏輯進行正確查詢 - 每個請求的日期產生1行。但 - 對08/14/2015日期的查詢不會產生任何結果。我現在正處於-04:00時區,如果這很重要的話。如果我將行數據 [end edit]中的時區[編輯] 更改爲-04:00,則2015年2月8日的行正確查詢。

我試圖找到答案,但已經幹了。有人可以權衡我的問題嗎?


[更新]:解決方法 這是一個被@PiotrAuguscik建議將查詢轉換到一個列表基於從this thread的建議解決方法(現在):

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]答案馬特 - 約翰遜

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

如何放置斷點並查看DbFunctions.TruncateTime(x.OrderDateLoc)返回的內容? –

+0

@ZoffDino - 感謝您的回覆。由於DbFunctions()函數只能出現在一個LINQ to Entities查詢中,所以我不知道除了我目前正在做的事情之外,我將如何做任何事情。請讓我知道,如果我嚴重忽略了這樣做的方式。 – HumbleBeginnings

+0

如果您使用的是SQL Server,是否使用了Profiler工具來準確查看SQL執行的內容?我猜測所比較的實際值並不完全符合您的預期。有些東西可能正在根據時區進行更改。 – jmcilhinney

回答

2

有幾件事情得出正確的解決方案代碼:

  • 兩個原始查詢和您的解決方法是non-sargable。您不應該在WHERE子句中操縱比較的左側。如果這樣做,數據庫不能使用任何索引,並且數據越多,數據越慢。相反,做一個範圍查詢。

  • 它會出現在您的表中有datetimeoffset類型。這些代表了特定的時刻,因此與兩個datetimeoffset值的比較是基於它們的UTC等值而不是它們的本地顯示時間。值也以這種方式編入索引。

  • 不是每個人都在同一時間觀察相同的日曆日期。你需要問自己,「我要求的是誰的約會?「

    • 如果它使查詢者的日期,那麼你的輸入值應該反映這一點。不是傳遞到您的查詢VB Date(這是一個System.DateTime)在當地時間而言,無論是通在DateTimeDateTimeOffset基於UTC-記住,你需要做一個範圍查詢,所以你會計算它們的,作爲一個半開區間換句話說:。

      // 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 
      
    • 如果你正在尋找匹配當地的da te,因爲它在您的SQL Server數據庫中有額外的工作要做。

      • 首先,您需要通過convert(datetime2, yourDateTimeOffset)到剝去偏移,或只是convert(date, yourDateTimeOffset)計算原當地日期。您應該在computed column中執行此操作,以便您還可以在其上創建索引。

      • 然後,您可以使用該計算列進行範圍查詢,或者如果計算到日期,則可以對此進行相等比較。

  • 一般情況下,我會避免在where子句中使用DbFunctions.TruncateTime。把它轉換到一些相當低效的SQL針對datetimeoffset場使用時,看起來像這樣:

    convert(datetimeoffset, convert(varchar(255), yourField, 102) + ' 00:00:00 ' + Right(convert(varchar(255), yourField, 121), 6), 102) 
    

    本質上講,這使用字符串來重新打造datetimeoffset同時保留了偏移,但時間設置到午夜,這是可能不是你真正想做的事情。你可以在SQL Profiler中自己看到。

+0

@Matt_Johnson - 非常感謝您的詳細解釋。這是非常有用的和令人難以置信的信息。可悲的是,我從來沒有想到按照你的建議去做......我做了,它的工作很完美,並且都符合你提到的指導原則。我用來解決問題的代碼已被編輯到我的OP中。我對你最好! – HumbleBeginnings

+0

很高興我能幫忙! –

相關問題