85

これが私が使用するものです:

SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME)

私は、より良い、よりエレガントな方法があるかもしれないと考えています。

要件:

  • できるだけ速くする必要があります (キャストが少ないほど良い)。
  • 最終結果はdatetime、文字列ではなく型でなければなりません。
4

6 に答える 6

117

SQL Server 2008 以降

もちろん、SQL Server 2008 以降では、最速の方法はConvert(date, @date). datetimeこれは、必要に応じてorにキャストできますdatetime2

SQL Server 2005 以前で本当に優れているのは?

SQL Server で日付から時間を切り詰めるのに最速の方法について一貫性のない主張を見てきました。また、テストしたと言う人もいますが、私の経験は異なります。そこで、もっと厳密なテストを行い、全員にスクリプトを渡して、間違いがあれば修正してもらいましょう。

浮動小数点数の変換は正確ではありません

まず、正しく変換されないため、への変換datetimeを避けます。時間の削除を正確に行うことでうまくいくかもしれませんが、これは安全な操作であり、そうではないことfloatを開発者に暗黙のうちに伝えるため、それを使用するのは悪い考えだと思います。見てみましょう:

declare @d datetime;
set @d = '2010-09-12 00:00:00.003';
select Convert(datetime, Convert(float, @d));
-- result: 2010-09-12 00:00:00.000 -- oops

これは、私たちのコードやオンラインの例で人々に教えるべきことではありません。

また、最速の方法でもありません!

証明 – 性能試験

いくつかのテストを自分で実行して、さまざまな方法が実際にどのように積み重なっていくかを確認したい場合は、さらに下のテストを実行するために、このセットアップ スクリプトが必要になります。

create table AllDay (Tm datetime NOT NULL CONSTRAINT PK_AllDay PRIMARY KEY CLUSTERED);
declare @d datetime;
set @d = DateDiff(Day, 0, GetDate());
insert AllDay select @d;
while @@ROWCOUNT != 0
   insert AllDay
   select * from (
      select Tm =
         DateAdd(ms, (select Max(DateDiff(ms, @d, Tm)) from AllDay) + 3, Tm)
      from AllDay
   ) X
   where Tm < DateAdd(Day, 1, @d);
exec sp_spaceused AllDay;  -- 25,920,000 rows

これにより、データベースに 427.57 MB のテーブルが作成され、実行に 15 ~ 30 分ほどかかることに注意してください。データベースが小さく、10% の成長に設定されている場合、最初に十分なサイズに設定した場合よりも時間がかかります。

次に、実際のパフォーマンス テスト スクリプトについて説明します。行をクライアントに返さないのは意図的であることに注意してください。これは 2600 万行で非常に高価であり、メソッド間のパフォーマンスの違いを隠してしまうからです。

パフォーマンス結果

set statistics time on;
-- (All queries are the same on io: logical reads 54712)
GO
declare
    @dd date,
    @d datetime,
    @di int,
    @df float,
    @dv varchar(10);

-- Round trip back to datetime
select @d = CONVERT(date, Tm) from AllDay; -- CPU time = 21234 ms,  elapsed time = 22301 ms.
select @d = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 23031 ms, elapsed = 24091 ms.
select @d = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23782 ms, elapsed = 24818 ms.
select @d = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 36891 ms, elapsed = 38414 ms.
select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 102984 ms, elapsed = 109897 ms.
select @d = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 103390 ms,  elapsed = 108236 ms.
select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 123375 ms, elapsed = 135179 ms.

-- Only to another type but not back
select @dd = Tm from AllDay; -- CPU time = 19891 ms,  elapsed time = 20937 ms.
select @di = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 21453 ms, elapsed = 23079 ms.
select @di = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23218 ms, elapsed = 24700 ms
select @df = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 29312 ms, elapsed = 31101 ms.
select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 64016 ms, elapsed = 67815 ms.
select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 64297 ms,  elapsed = 67987 ms.
select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 65609 ms, elapsed = 68173 ms.
GO
set statistics time off;

とりとめのない分析

これに関するいくつかのメモ。まず第一に、GROUP BY または比較を実行するだけであれば、元に戻す必要はありませんdatetime。したがって、表示目的で最終的な値が必要でない限り、それを回避することで CPU を節約できます。変換されていない値を GROUP BY して、変換を SELECT 句にのみ入れることもできます。

select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)

また、数値変換が に戻すのにわずかに時間がかかるだけでdatetimevarchar変換がほぼ 2 倍になることを確認してください。これにより、クエリで日付の計算に使用されている CPU の割合が明らかになります。日付計算を含まない CPU 使用率の部分があり、これは上記のクエリでは 19875 ミリ秒に近いように見えます。その後、変換には追加の金額がかかるため、2 つの変換がある場合、その金額は約 2 倍使用されます。

Convert(, 112)さらに調査すると、 と比較して、Convert(, 101)クエリの CPU 消費がいくらか追加されることがわかります (より長い?を使用するvarcharため)。ミリ秒の CPU 基本コスト。datevarcharConvert(, 112)

上記の分析に使用した CPU 時間の計算は次のとおりです。

     method   round  single   base
-----------  ------  ------  -----
       date   21324   19891  18458
        int   23031   21453  19875
   datediff   23782   23218  22654
      float   36891   29312  21733
varchar-112  102984   64016  25048
varchar-101  123375   65609   7843
  • roundは、 への往復の CPU 時間ですdatetime

  • singleは、代替データ型 (時間部分を削除する副作用があるもの) への 1 回の変換の CPU 時間です。

  • basesingleは、2 つの呼び出しの差から減算する計算です: single - (round - single). これは、そのデータ型との間の変換を想定した概算図datetimeであり、どちらの方向でもほぼ同じです。この仮定は完全ではないようですが、1 つの例外を除いてすべての値が 20000 ミリ秒に近いため、ほぼ一致しています。

もう 1 つの興味深い点は、基本コストが単一Convert(date)メソッドとほぼ同じであることです (サーバーはデータ型の最初の 4 バイトから整数の日の部分を内部的に抽出できるため、コストはほぼ 0 でなければなりませんdatetime)。

結論

すると、片方向varchar変換方式で約1.8μs、片方向変換方式で約0.18μsということになりDateDiffます。これは、25,920,000 行で合計 18458 ミリ秒のテストで最も保守的な「ベース CPU」時間に基づいているため、23218 ミリ秒 / 25920000 = 0.18 μs です。明らかな 10 倍の改善はかなりのように思えますが、率直に言って、数十万行を処理するまではかなり小さいものです (617k 行 = 1 秒の節約)。

この小さな絶対的な改善があったとしても、私の意見でDateAddは、パフォーマンスと明快さの最良の組み合わせであるため、この方法が勝ちます。の「マジック ナンバー」を必要とする答えは、0.50000004いつか誰かを噛むことになるでしょう (5 つのゼロまたは 6 ???)、それに加えて、理解するのがより困難です。

その他の注意事項

時間ができたら着替え0.50000004'12:00:00.003'様子を見ようと思います。同じ値に変換され、datetime覚えやすくなっています。

興味のある方のために、上記のテストは @@Version が以下を返すサーバーで実行されました。

Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) 2008 年 7 月 9 日 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2)

于 2010-09-12T22:57:35.493 に答える
30

SQL Server 2008には新しい日付データ型があり、これによりこの問題が次のように単純化されます。

SELECT CAST(CAST(GETDATE() AS date) AS datetime)
于 2008-08-06T06:44:32.600 に答える
18

Itzik Ben-Gan in DATETIME Calculations、Part 1(SQL Server Magazine、2007年2月)は、このような変換を実行する3つの方法を示しています(最も遅い方法から最も速い方法、2番目と3番目の方法の違いは小さい)。

SELECT CAST(CONVERT(char(8), GETDATE(), 112) AS datetime)

SELECT DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0)

SELECT CAST(CAST(GETDATE() - 0.50000004 AS int) AS datetime)

あなたのテクニック(フロートへのキャスト)は、雑誌の4月号の読者によって提案されています。彼によると、それは上に提示された2番目の技術のそれに匹敵する性能を持っています。

于 2008-08-06T08:06:19.720 に答える
12

あなたのCAST--少なくとも MS SQL Server 2005 では、すでに最適な方法のようですFLOORCAST

私が見た他のいくつかのソリューションにはSelect Convert(varchar(11), getdate(),101)、それらのように文字列変換があり、10 倍遅くなります。

于 2008-08-05T20:12:29.037 に答える
4

してみてください:

SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME]
于 2013-06-29T09:49:05.130 に答える
1

SQL2005: dateadd の代わりにキャストをお勧めします。例えば、

select cast(DATEDIFF(DAY, 0, datetimefield) as datetime)

私のデータセットでは平均して約 10%高速です。

select DATEADD(DAY, DATEDIFF(DAY, 0, datetimefield), 0)

(そして、smalldatetimeへのキャストはさらに高速でした)

于 2014-11-05T04:26:40.977 に答える