2008-08-05 25 views
75

は、ここで私が使用しているものです:datetime値(SQL Server)の時間部分を削除する方法はありますか。

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

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

要件:

  • それは(より良い、より少ない鋳造)可能な限り速くなければなりません。
  • 最終結果は文字列ではなくdatetimeタイプでなければなりません。

答えて

103

SQL Server 2008およびアップ

SQL Server 2008では、最大は、Convert(date, @date)です。必要に応じてこれをdatetimeまたはdatetime2にキャストバックすることができます。

SQL Server 2005以降では最高のものは何ですか?

SQL Serverの日付から時刻を切り捨てることについて最も矛盾していることについて、私は一貫性のない主張を見てきました。だからもっと厳しいテストをしてみましょう。そして誰もがスクリプトを作って、私が間違いを犯すと人々が私を修正できるようにしましょう。

フロートの変換は、それが正しく変換されないので、私は、floatdatetime変換から離れて滞在する、

まず正確ではありません。除外対象を正確に実行することで逃げるかもしれませんが、安全な操作であることを暗黙的に開発者に伝えており、ではなくであるため、使用することは悪い考えです。見てみよう:

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%の成長率に設定されている場合は、最初に十分な大きさのサイズよりも時間がかかります。

実際のパフォーマンステストスクリプト用です。 2千6百万行で高価であり、メソッド間のパフォーマンスの違いを隠すため、行をクライアントに返さないことが目的であることに注意してください。

パフォーマンスが

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を節約してCPUを節約することができます。あなたは未変換値BYでもGROUPとは、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) 

は、数値変換だけdatetimeに戻って変換するために少し時間がかかるかを確認しますが、varchar変換は、ほぼ倍増しますか?これは、クエリの日付計算に費やされるCPUの部分を明らかにします。 CPU使用量に日付計算が含まれていない部分がありますが、これは上記のクエリで19875   msに近いものです。その後、コンバージョンにいくらかの追加費用がかかるため、コンバージョンが2回ある場合、その金額は約2回使い切られます。

より検査は、(それが長いvarchar?使用しているので)バックdateに第2の変換はvarcharに初期変換と同じくらい費用がかかりませんので、Convert(, 112)に比べ、Convert(, 101)クエリは、いくつかの追加のCPU費用を持っていることが明らかになりましたConvert(, 112)と同じ20000  ミリ秒CPUベースコストに近いです。ここで

は、私は上記の分析のために使用される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 
  • ラウンドはバックdatetimeへの往復のCPU時間です。

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

  • ベースsingle 2回の呼び出しの間の差から減算の計算です。これは、そのデータ型との間の変換を想定した球体図であり、datetimeはいずれの方向でもほぼ同じです。この仮定は完璧ではないと思われますが、値はすべて1つの例外を除いて20000   msに近いため、近いです。

興味深いものは、サーバが内部右最初の4バイトのうちの整数日の部分を抽出することができるようにベースコストはほぼ0コストでなければならない単一Convert(date)方法(、ほぼ等しいことがよりデータ型の)。

結論

だから、それがどのように見えるの単方向varchar変換方法は、約1.8マイクロ秒 を取り、単一方向DateDiff方法は、約0.18  マイクロ秒を要することです。私はこれを、最も厳密な「ベースCPU」の時間に基づいています。これは、私のテストで18458  ms25,920,000行の合計であるため、23218   ms/25920000 = 0.18  μsです。見かけ上の10倍の改善はたくさんあるようですが、数十万行(617k行= 1秒の節約)を扱うまでは、率直にかなり小さいです。

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

追加メモ

私は'12:00:00.003'0.50000004を変更し、それがないかを確認するつもりだいくつかの時間を取得。それは同じdatetimeの値に変換され、私はそれを覚えるほうがずっと簡単です。

のMicrosoft SQL Server 2008の(RTM) - 10.0.1600.22(インテルX86)2008年7月9日14:興味のある方のために

は、上記のテストは、バージョンは以下を返す@@サーバー上で実行されました: 43:34著作権(C)1988年から2008年、米国Microsoft CorporationのStandard EditionのWindows NT 5.2上の(ビルド3790:サービスパック2)

+0

+1途中でこれをどのバージョンのSQL Serverでテストしましたか? –

+0

テーブルに* single *と* round *があるようです。また、 'varchar'の代わりに' char'を使うと時間的に違いはありますか? – Gabe

+0

@Gabeありがとう、固定。 Charはvarcharとまったく同じように見えます。 – ErikE

11

あなたCAST - FLOOR - CASTすでに最適な方法であると思われる、少なくともMS SQL Server上で私が見た2005年

いくつかの他のソリューションには、それらの中にSelect Convert(varchar(11), getdate(),101)のように、文字列の変換を持っています、 10.

+2

これはかなりにより、最適な方法ではありません少し。 [私の答え](http://stackoverflow.com/questions/2775/whats-the-best-way-to-remove-the-time-portion-of-a-datetime-value-sql-server/3696991)を見てください。 #3696991)を同じページに表示します。 – ErikE

+0

私たちの製品でMichael Stumが提案した方法を使用し、それは魅力的に機能します。 –

27

SQL Server 2008の持つ新しいdate data type倍遅くなり、これはこの問題を単純化する:

SELECT CAST(CAST(GETDATE() AS date) AS datetime) 
16

Itzik Ben-Gan DATETIME Calculations, Part 1(SQL Server Magazine、February 2007)は、このような変換を実行する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の技術と同等の性能を有する。もちろん最速の方法

+0

私の意見では、浮動小数点へのキャストは最善ではありません。してください[私の答えを参照してください](http://stackoverflow.com/questions/2775/whats-the-best-way-to-remove-the-time-portion-of-a-datetime-value-sql-server/3696991 #3696991) – ErikE

+0

@Emtucifor私は、第3の方法が* 0.50000004 *値のために非常に不明瞭であることに同意しますが、**最も速いものです。従って、*可能な限り*の要件を満たす*。 –

+0

@Emtuciforまた、私がリンクしている記事では、* 0.50000004 *の値について述べています:*この表現は短く(やや詳しく説明しますが)効率的ですが、**私はそれが不安であると言わざるを得ません* *。私は正確に何かを指摘することができるのかどうか確信が持てません。それは技術的で、日時に関連するロジックが見えないからです。* –

3

てみてください:

SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME] 
0

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

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

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

よりも、私のデータセットに約10%速く平均(およびsmalldatetime型にキャストすると、より速くまだあった)

関連する問題