31

私が抱えている問題は次のとおりです。2 つの日付が同じ日であるかどうかを確認するために、where 句で日時を比較する必要がある大きなクエリがあります。私の現在の解決策は、日時をUDFに送信して同じ日の真夜中に変換し、それらの日付が等しいかどうかを確認することです。クエリ プランに関して言えば、結合または where 句のほとんどすべての UDF と同様に、これは惨事です。これは、関数を根こそぎにして、最適なインデックスを見つけるために実際に使用できるものをクエリ オプティマイザーに与えることができなかったアプリケーション内の唯一の場所の 1 つです。

この場合、関数コードをクエリにマージすることは非現実的です。

ここで単純なものが欠けていると思います。

参考までに機能はこちら。

if not exists (select * from dbo.sysobjects 
              where id = object_id(N'dbo.f_MakeDate') and               
              type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
  exec('create function dbo.f_MakeDate() returns int as 
         begin declare @retval int return @retval end')
go

alter function dbo.f_MakeDate
(
    @Day datetime, 
    @Hour int, 
    @Minute int
)
returns datetime
as

/*

Creates a datetime using the year-month-day portion of @Day, and the 
@Hour and @Minute provided

*/

begin

declare @retval datetime
set @retval = cast(
    cast(datepart(m, @Day) as varchar(2)) + 
    '/' + 
    cast(datepart(d, @Day) as varchar(2)) + 
    '/' + 
    cast(datepart(yyyy, @Day) as varchar(4)) + 
    ' ' + 
    cast(@Hour as varchar(2)) + 
    ':' + 
    cast(@Minute as varchar(2)) as datetime)
return @retval
end

go

問題を複雑にするために、タイムゾーンテーブルに参加して、日付を現地時間と比較しています。これは、行ごとに異なる可能性があります。

where 
dbo.f_MakeDate(dateadd(hh, tz.Offset + 
    case when ds.LocalTimeZone is not null 
    then 1 else 0 end, t.TheDateINeedToCheck), 0, 0) = @activityDateMidnight

[編集]

@Todd の提案を取り入れています。

where datediff(day, dateadd(hh, tz.Offset + 
    case when ds.LocalTimeZone is not null 
    then 1 else 0 end, t.TheDateINeedToCheck), @ActivityDate) = 0

datediff がどのように機能するかについての私の誤解 (連続した年で同じ年が 366 になり、予想どおり 0 ではありません) により、多くの労力が無駄になりました。

しかし、クエリ プランは変更されませんでした。全体として、最初からやり直す必要があると思います。

4

10 に答える 10

73

これははるかに簡潔です:

where 
  datediff(day, date1, date2) = 0
于 2008-08-22T15:02:46.300 に答える
21

where句の左側をきれいに保つ必要があります。したがって、通常、次のようなことを行います。

WHERE MyDateTime >= @activityDateMidnight 
      AND MyDateTime < (@activityDateMidnight + 1)

(一部の人々は代わりにDATEADD(d、1、@activityDateMidnight)を好みます-しかしそれは同じことです)。

ただし、TimeZoneテーブルは問題を少し複雑にします。スニペットからは少しわかりにくいですが、t.TheDateInTableはGMTでタイムゾーン識別子を使用しており、オフセットを追加して@activityDateMidnightと比較しているようです。これは現地時間です。ただし、ds.LocalTimeZoneが何であるかはわかりません。

その場合は、代わりに@activityDateMidnightをGMTに設定する必要があります。

于 2008-08-22T15:16:00.980 に答える
6
where
year(date1) = year(date2)
and month(date1) = month(date2)
and day(date1) = day(date2)
于 2008-08-22T14:56:16.363 に答える
4

オプティマイザーが日付をいじるときにインデックスを効果的に利用できるように、数行のコードを変更することで1000%以上の改善を得ることができるデータベースでのみ読むようにしてください

于 2008-08-22T15:21:21.107 に答える
2

これにより、日付から時間コンポーネントが削除されます。

select dateadd(d, datediff(d, 0, current_timestamp), 0)
于 2008-08-22T15:06:59.410 に答える
2

Eric Z Beard:

I do store all dates in GMT. Here's the use case: something happened at 11:00 PM EST on the 1st, which is the 2nd GMT. I want to see activity for the 1st, and I am in EST so I will want to see the 11PM activity. If I just compared raw GMT datetimes, I would miss things. Each row in the report can represent an activity from a different time zone.

Right, but when you say you're interested in activity for Jan 1st 2008 EST:

SELECT @activityDateMidnight = '1/1/2008', @activityDateTZ = 'EST'

you just need to convert that to GMT (I'm ignoring the complication of querying for the day before EST goes to EDT, or vice versa):

Table: TimeZone
Fields: TimeZone, Offset
Values: EST, -4

--Multiply by -1, since we're converting EST to GMT.
--Offsets are to go from GMT to EST.
SELECT @activityGmtBegin = DATEADD(hh, Offset * -1, @activityDateMidnight)
FROM TimeZone
WHERE TimeZone = @activityDateTZ

which should give you '1/1/2008 4:00 AM'. Then, you can just search in GMT:

SELECT * FROM EventTable
WHERE 
   EventTime >= @activityGmtBegin --1/1/2008 4:00 AM
   AND EventTime < (@activityGmtBegin + 1) --1/2/2008 4:00 AM

The event in question is stored with a GMT EventTime of 1/2/2008 3:00 AM. You don't even need the TimeZone in the EventTable (for this purpose, at least).

Since EventTime is not in a function, this is a straight index scan - which should be pretty efficient. Make EventTime your clustered index, and it'll fly. ;)

Personally, I'd have the app convert the search time into GMT before running the query.

于 2008-08-22T16:33:13.243 に答える
1

ここでのオプションに関しては、選択に甘んじています。SybaseまたはSQLServer2008を使用している場合は、dateタイプの変数を作成し、それらに日時値を割り当てることができます。データベースエンジンはあなたのために時間を取り除きます。これが説明するための簡単で汚いテストです(コードはSybase方言にあります):

declare @date1 date
declare @date2 date
set @date1='2008-1-1 10:00'
set @date2='2008-1-1 22:00'
if @date1=@date2
    print 'Equal'
else
    print 'Not equal'

SQL 2005以前の場合、実行できることは、日付を時間コンポーネントを持たない形式のvarcharに変換することです。たとえば、次のように2008.08.22が返されます。

select convert(varchar,'2008-08-22 18:11:14.133',102)

102の部分はフォーマットを指定します(オンラインの本はあなたのために利用可能なすべてのフォーマットをリストすることができます)

したがって、実行できるのは、日時を取得し、日付要素を抽出して時刻を破棄する関数を作成することです。そのようです:

create function MakeDate (@InputDate datetime) returns datetime as
begin
    return cast(convert(varchar,@InputDate,102) as datetime);
end

その後、コンパニオンの機能を使用できます

Select * from Orders where dbo.MakeDate(OrderDate) = dbo.MakeDate(DeliveryDate)
于 2008-08-22T15:22:51.087 に答える
1

エリック・Z・ビアード:

アクティビティの日付は現地のタイム ゾーンを示すためのものであり、特定のタイム ゾーンを示すものではありません

わかりました - 製図板に戻ります。これを試して:

where t.TheDateINeedToCheck BETWEEN (
    dateadd(hh, (tz.Offset + ISNULL(ds.LocalTimeZone, 0)) * -1, @ActivityDate)
    AND
    dateadd(hh, (tz.Offset + ISNULL(ds.LocalTimeZone, 0)) * -1, (@ActivityDate + 1))
)

@ActivityDate を現地時間に変換し、それと比較します。これがインデックスを使用する最善の方法ですが、うまくいくかどうかはわかりません。試して、クエリ プランを確認してください。

次のオプションは、インデックス付きの計算された TimeINeedToCheckを現地時間で使用するインデックス付きビューです。次に、次の場所に戻ります。

where v.TheLocalDateINeedToCheck BETWEEN @ActivityDate AND (@ActivityDate + 1)

これは間違いなくインデックスを使用します-ただし、INSERTとUPDATEにわずかなオーバーヘッドがあります。

于 2008-08-23T02:14:08.480 に答える
0

datepartのdayofyear関数を使用します。


Select *
from mytable
where datepart(dy,date1) = datepart(dy,date2)
and
year(date1) = year(date2) --assuming you want the same year too

ここでdatepartリファレンスを参照してください。

于 2008-08-22T15:27:02.123 に答える
0

タイムゾーンに関しては、すべての日付を単一のタイムゾーン(できればUTC)に保存するもう1つの理由があります。とにかく、datediff、datepart、およびさまざまな組み込みの日付関数を使用した回答が最善の策だと思います。

于 2008-08-22T15:31:35.637 に答える