SQL Serverで日時の時間部分を削除する最善の方法


514

SQL Serverの日時フィールドから時間部分を削除するときに、どの方法が最高のパフォーマンスを提供しますか?

a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)

または

b) select cast(convert(char(11), getdate(), 113) as datetime)

2番目の方法は、どちらの方法でも数バイトを送信しますが、変換の速度ほど重要ではない場合があります。

どちらも非常に高速に見えますが、数十万行以上を処理するときに速度に違いがあるのではないでしょうか。

また、SQLの日時の時間部分を取り除くためのさらに優れた方法がある可能性はありますか?


1
私は本番テーブルの1つで100万件のレコードを試してみましたが、どちらの方法でもパフォーマンスを正確に読み取ることができませんでした。ただし、どちらの方法でもまったく同じ量のデータが返されました。
スティーブンペレルソン2009

9
18,000,000行では、これは私が見つけたものです(SQL Server 2008):メソッドbはメソッドaよりも約24%遅いです。CAST(FLOOR(CAST(getdate()AS FLOAT))AS DATETIME)は、メソッドaよりも3.5%遅くなります。メソッドaは、パフォーマンスに関しては勝者のようです。素晴らしい答えをありがとう。
スティーブンペレルソン2009

46
なぜSQLにはこれを行うための組み込み関数がないのですか?
ゲイリーマギル

10
SQL 2008の新しいDATEデータ型がこれを処理します。
フィリップケリー

回答:


558

厳密に言えば、メソッドaは最もリソースを消費しません。

a) select DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)

同じ合計期間で100万行のCPUをあまり消費しないことが証明されており、手作業が多すぎる場合:SQL Serverで日付と時刻から日付を取得する最も効率的な方法は?

他の場所でも同様のテストを見て、同様の結果を得ました。

私はDATEADD / DATEDIFFを好みます。

  • varcharは言語/日付形式の問題の影響を受けます
    例:なぜ私のCASE式は非決定的ですか?
  • floatは内部ストレージに依存しています
  • 「0」ベースを変更することにより、月の初日、明日などのワークアウトに拡張

編集、2011年10月

SQL Serverの2008+について、あなたはにキャストすることができますdateすなわちCAST(getdate() AS date)。または、dateデータ型を使用して、削除する時間がないようにします。

編集、2012年1月

これがいかに柔軟であるかを示す実例:SQLサーバーで丸められた時間または日付の数値で計算する必要がある

編集、2012年5月

考えずにWHERE句などでこれを使用しないでください。関数またはCASTを列に追加すると、インデックスの使用が無効になります。ここ2を参照してください:http : //www.simple-talk.com/sql/t-sql-programming/ten-common-sql-programming-mistakes/

現在、これには、CASTを正しく日付管理する新しいSQL Serverオプティマイザーバージョンの例がありますが、 一般的には悪い考えです...

編集、2018年9月、datetime2

DECLARE @datetime2value datetime2 = '02180912 11:45' --this is deliberately within datetime2, year 0218
DECLARE @datetime2epoch datetime2 = '19000101'

select DATEADD(dd, DATEDIFF(dd, @datetime2epoch, @datetime2value), @datetime2epoch)

3
2011年10月の編集用の@David Sopkoの場合、コードは次のようになります。selectcast(GETDATE()as date)
Choco Smith

1
最近のバージョンのSQLでは、datetimeの代わりに日付を使用すると、時間を扱う必要がなくなります。次のサンプルを使用します。noTime日付を宣言する= getdate()、withTime datetime = getdate()select @ noTime、@ withTime
ozkary

1
日付が必要な場合は、日付としてのキャストが最適です。ただし、多くの場合、現在の日付を真夜中に必要とするため、さらに日付を操作できます。DATEデータ時間は、それはあなたがDATEADD、DATEDIFF、およびその他の日付/時刻データ型との相互作用のようなものに関連して行うようになる何で不愉快に制限されています。それらのケースでは、DATEADD()アプローチは王を支配します。
Xedni 2017

これはすべての日付で機能するわけではありません。年の0218代わりに誤って入力2018したためDATEDIFF、ステートメントの一部で例外がスローされますThe conversion of a datetime2 data type to a datetime data type resulted in an out-of-range datetime value試してみてくださいselect DATEDIFF(dd, 0, convert(datetime2(0), '0218-09-12', 120))
BernhardDöblerSep

1
@BernhardDöblerが2009年7月に私が答えたとき、「0218」は有効な日付だったので、ここまでは得られなかったでしょう。また、「0」はdatetime2の19000101に変換されません。この選択をお試しくださいSELECT DATEDIFF(dd, '19000101', convert(datetime2(0), '0218-09-12', 120))
gbn

69

SQL Server 2008では、以下を使用できます。

CONVERT(DATE, getdate(), 101)

13
3番目の引数は、aからa datetimeへの変換時の結果にはまったく関係がないdateため、ソリューションは実質的にjust CONVERT(DATE,getdate())に要約されますが、これはすでに複数回提案されています。
Andriy M

価値がないと私が思うだけ、CAST(GETDATE() AS DATE)または厳密にANSI CAST(CURRENT_TIMESTAMP AS DATE)を使用します。最初のものにとどまります。
Ivanzinho 2018

52

もちろんこれは古いスレッドですが、完全にするためです。

SQL 2008以降では、DATEデータ型を使用できるため、次のように簡単に実行できます。

SELECT CONVERT(DATE,GETDATE())

21
SELECT CAST(FLOOR(CAST(getdate() AS FLOAT)) AS DATETIME)

...ではない以下のコメントによると、良い解決策で。

この回答は削除しますが、なぜそれが良いアイデアではないのについてのコメント投稿者の説明は依然として有用だと思うので、ここでは反例として残しておきます。


GBNの答えを見てください、多くはこれを調査しました。DATETIMEは浮動小数点数として格納されないため、DATEADD / DATEDIFFを使用すると、タイプ間でCASTする数学的な操作が不要になります。
MatBailie 2009

説明した理由により、DATETIMEからFLOATへのキャストを避けたいと思うかもしれませんが、その場合は、OPsオプション(a)でのゼロからの暗黙的な変換も問題ではありませんか?うーん...その場合、それはFLOATではなく、サーバーはおそらく時間情報を破棄するのに十分スマートだと思います。承知しました:-)
ゲイリー・マギル

確かに0は、数値型(私が推測するINT)からDATETIMEへの暗黙的な変換です。ただし、定数式であるため、オプティマイザはストアドプロシージャのコンパイル時にそれを実行でき、SQLを動的に実行するために一度だけ実行する必要があります。つまり、そのためのオーバーヘッドは1回だけです。FLOATベースのクエリには、すべての行に対して同等のオーバーヘッドがあります。
MatBailie 2009

フロートへのキャストは非常に不正確です。この回答は削除する必要があります。誰もこのコードを使用するべきではありません。
usr

3
floatにキャストしてdatetimeに戻すのは安全ではないことは言うまでもありません。floatの精度は十分ではありません。だからお勧めできないと思います。詳細については、この投稿を参照してください
ErikE 2013年

17

SQL Server 2008には、DATE日付型(TIMEデータ型)があります。

CAST(GetDate() as DATE)

または

declare @Dt as DATE = GetDate()

これは私が使ったもので、うまくいきました。最も単純な答えのようです。CONVERTと組み合わせて使用​​することの欠点は何ですか?
joelmdev

1
CASTとCONVERTの機能は同じです。違いは、CASTはANSI規格の一部であるのに対し、CONVERTはT-SQLに固有のものであることです。したがって、可能な限りCASTを使用してください。
トロイ2018

@troy 3つの入力文字を保存できるのでCASTを使用し、構文はCONVERTよりも明確です
。ANSI

8

これは、別の重複した質問からの別の回答です:

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

このマジックナンバーメソッドは、DATEADDメソッドよりもわずかに高速に実行されます。(約10%のように見えます)

100万レコードの数ラウンドのCPU時間:

DATEADD   MAGIC FLOAT
500       453
453       360
375       375
406       360

ただし、これらの数値はすでに非常に高速であるため、無関係である可能性があることに注意してください。100,000以上のレコードセットがなければ、CPU時間をゼロ以上に読み取ることもできませんでした。

DateAddはこの目的のためのものであり、より堅牢であるという事実を考慮すると、DateAddを使用すると思います。


1
それは恐ろしいことです。このような危険にさらされることはありません。これがテストしたものだけでなく、すべての日時に対して正しいかどうかは誰にもわかりません。
usr

@usrああ、それは正しいです。それは単なるマジックナンバーであり、そのため使用すべきではありません。その正確さを確認したい場合は、1日のすべての可能な日付をテーブルに入力し、結果を確認してください!詳細については、この投稿参照してください。
ErikE 2013年

@ErikE良い点。'12:00:00.003'しかし、あなたの答えは、私がはるかに優れていると思う使用の可能性を提供します。
usr

6
SELECT CAST(CAST(GETDATE() AS DATE) AS DATETIME)

4
はい、有効なオプションです。ただし、このスレッドで複数回提案されています。
Andriy M

正直なところ、このソリューションは最も読みやすいものです。私の後藤
BelgoCanadian

5

私は本当に好きです:

[date] = CONVERT(VARCHAR(10), GETDATE(), 120)

120書式コードは、ISO 8601標準に日付を強制します。

'YYYY-MM-DD' or '2017-01-09'

dplyr(R)とpandas()で非常に使いやすいPython


3

注意してください!

方法a)とb)は常に同じ出力になるとは限りません!

select DATEADD(dd, DATEDIFF(dd, 0, '2013-12-31 23:59:59.999'), 0)

出力: 2014-01-01 00:00:00.000

select cast(convert(char(11), '2013-12-31 23:59:59.999', 113) as datetime)

出力: 2013-12-31 00:00:00.000

(MS SQL Server 2005および2008 R2でテスト済み)

編集:Adamのコメントによると、これはテーブルから日付値を読み取る場合は発生しませんが、日付値をリテラルとして提供する場合に発生する可能性があります(例:ADO.NET経由で呼び出されるストアドプロシージャのパラメーターとして)。


1
.999はSQL ServerのDATETIME列に格納できません。利用可能な最高値は.997です。msdn.microsoft.com/ en- us / library / ms187819.aspxの値は、0、3、または7に1000桁になるように丸められていることがわかります。OPには表示されません。それらのテーブルのテストからの値。
Adam Wenger

あなたは正しいです。私はこれをOPの質問への回答として投稿するつもりはありませんでしたが、他の人が見るためのコメントとして投稿しましたが、評判ポイントは11しかなく、コメントには15が必要です。
ブロスラフ2014年

最初のスニペットでは、文字列定数は暗黙的に日時に変換され、2番目のスニペットでは文字列のままです(113は無視されます)。
Andriy M

2

そもそも挿入/更新の時間を取り除きます。オンザフライ変換については、ユーザー定義関数の保守性の面で勝るものはありません。

select date_only(dd)

の実装はdate_only好きなようにすることができます-今では抽象化されており、コードを呼び出すことははるかにきれいです。


選択した列から時間をスクラブするトリガーを考案しました。データが悪くない場合は、データを消去する必要はありません。
フィリップケリー

2
UDFアプローチには欠点があります。SARGには対応できません。JOINまたはWHERE句で使用する場合、オプティマイザはINDEXesを使用してパフォーマンスを向上させることはできません。ただし、DATEADD / DATEDIFFアプローチの使用はSARGableであり、INDEXから利益を得ることができます。(どうやらFLOATメソッドもSARG可能です)
MatBailie

1
@MatBailie私は違うことを懇願します!UDFは確実にSARG可能ではありませんが、DateaddでもConvert to floatでもありません!WHERE DateAdd(DateDiff(Column)) = @DateValueインデックスを使用しません。一方、WHERE Column >= dbo.UDF(@DateValue) AND Column < dbo.UDF(@DateValue + 1) ある検索引数可能。だからそれを置く方法に注意してください。
ErikE 2013年

2

この質問を参照してください:
SQL Serverで日時を切り捨てる方法を教えてください。

何をする場合でも、文字列メソッドを使用しないください。それはあなたがそれを行うことができる最悪の方法についてです。


おかげで、これは以前に尋ねられたはずだと思いました。奇妙なことに、SQL Server 2008では、floatメソッドは実際にdateadd(dd、0、datediff(dd、0、getDate()))メソッドよりも3.5%遅いことが指摘されています。メソッドごとに何度もテストを実行しましたが、そのときデータベースサーバーは他の何にも使用されていませんでした。
スティーブンペレルソン2009

仕事の一環として定期的かつ非常に科学的な方法でベンチマークを行っていることを実証していない人が行ったベンチマークには懐疑的だと言ってください。gbnによるリンクのThomasのベンチマークでさえ、それを見ると明らかな問題がいくつかあります。それは必ずしも間違いではありません、決定的なものではありません。キャスト/フロア/キャストの方法は、非常に長い間、受け入れられている最速の方法でした。そうは言っても、私はそれを再考し始めています。特にSQL Server 2008の場合は、とにかく完全に不要です。
Joel Coehoorn、2009

1
文字列メソッドは、非常に使いやすく、読みやすく、覚えやすいです。これらはあなたが過小評価していると私が思う非常に重要な要素です!
ベン

1
@JoelCoehoorn、変換スタイル121は「ODBC Canonical」と呼ばれます。照合順序やロケールによって異なりません。文字列のトリックは、年、年+月、日、時間、分に一般化するのも簡単です。
ベン・

1
@Ben文字列トリックは開発者に文字列変換を使用するように教えます。それらは機能しますが、日付の計算は多くの理由ではるかに優れています。その理由の少なくとも1つはスピードですが、それ以上に、日付としての数値を使用する学習は、開発者と彼の精神能力にコード内の数値操作で流動的である。
ErikE 2013年




2

CONVERT(char(10)、GetDate()、126)を選択します


@broslavの回答に記載されている方法、またはこのスレッドで最も遅いと判断された方法(承認された回答と同じリンク)からの提案の主な違いは何ですか?
Andriy M

1

ってことだと思う cast(floor(cast(getdate()as float))as datetime)

実数は32ビットのみであり、一部の情報が失われる可能性があります

これは最速です cast(cast(getdate()+x-0.5 as int)as datetime)

...ただし、約10%高速化(about 0.49 microseconds CPU vs. 0.58)

これは推奨されており、私のテストでは今と同じ時間がかかります: DATEADD(dd, DATEDIFF(dd, 0, getdate()), 0)

SQL 2008では、SQL CLR関数はSQL関数を使用する場合よりも約5倍高速であり、6.5マイクロセクションに対して1.35マイクロ秒であり、単純なSQL UDFに対してSQL CLR関数の関数呼び出しオーバーヘッドがはるかに低いことを示しています。

SQL 2005では、SQL CLR関数は、この遅い関数と比較して、私のテストでは16倍高速です。

create function dateonly (  @dt datetime )
returns datetime
as
begin
return cast(floor(cast(@dt as float))as int)
end

1

いかがselect cast(cast my_datetime_field as date) as datetime)ですか?これにより、時刻が00:00に設定された同じ日付になりますが、テキストへの変換が回避され、明示的な数値の丸めも回避されます。



彼らは同じではありません。他の回答は、それを時間コンポーネントのない日付にキャストし、そのままにしておくことを提案しました。私の投稿では、午前0時の日時に設定されています。大きな違いがあります。MS Excelにエクスポートしてみてください。日付よりも日時がはるかによく処理されることがわかります。
ドリュー博士

最初のものはまったく同じです。
ミカエルエリクソン2014年

はい、わかりました。必要に応じて、回答を重複して削除させていただきます。
ドリュー博士

1

あなたがそれに厳密に固執すればTSQL、これが時間を短縮する最も速い方法だと思います:

 select convert(datetime,convert(int,convert(float,[Modified])))

この切り捨て方法は、この方法より約5%高速であることがわかりましたDateAdd。これは、次のように最も近い日に丸めるように簡単に変更できます。

select convert(datetime,ROUND(convert(float,[Modified]),0))


1

ここでは、SQL Serverの日時の一部を削除する関数を作成しました。使用法:

  • 最初のパラメーターは、削除される日時です。
  • 2番目のパラメーターは文字です。
    • s:秒に丸めます。ミリ秒を削除します
    • m:分に丸めます。秒とミリ秒を削除します
    • h:時間に丸めます。分、秒、ミリ秒を削除します。
    • d:日数に丸めます。時間、分、秒、ミリ秒を削除します。
  • 新しい日時を返します

create function dbo.uf_RoundDateTime(@dt as datetime, @part as char) returns datetime as begin if CHARINDEX( @part, 'smhd',0) = 0 return @dt; return cast( Case @part when 's' then convert(varchar(19), @dt, 126) when 'm' then convert(varchar(17), @dt, 126) + '00' when 'h' then convert(varchar(14), @dt, 126) + '00:00' when 'd' then convert(varchar(14), @dt, 112) end as datetime ) end


アンドリー、ありがとう!私の推薦がそれほど効率的でないことを知りませんでした。少なくともそれは機能しますが、あなたは正しいです。
Max Vargas

1

上記のバージョンのいくつかが機能しなかったので、誰かがSybaseバージョンをここで探している場合に備えて

CAST(CONVERT(DATE,GETDATE(),103) AS DATETIME)
  • Adaptive Server 15.7で動作するSQL v11でテスト済み

これは、受け入れられた回答の編集として適しています。他の20の答えで、これは埋もれて、見つけることができなくなります。また、受け入れられた答えは、使用について言及していcastます:SQL Server 2008以降の場合、これまでにキャストできます。または、日付を使用して、削除する時間がないようにします。
EWit 2014

これを、Sybaseの同等の質問への回答として投稿することをお勧めします。そのような質問がない場合は、自由に作成できます(自分で答えてください)。
Andriy M

また、a datetimeを変換するときにCONVERTに3番目のパラメーターを指定しても意味がありdateません。どちらにも固有の形式はありません。
Andriy M

0

可能であれば、このような特別なことのために、CLR関数を使用するのが好きです。

この場合:

[Microsoft.SqlServer.Server.SqlFunction]
    public static SqlDateTime DateOnly(SqlDateTime input)
    {
        if (!input.IsNull)
        {
            SqlDateTime dt = new SqlDateTime(input.Value.Year, input.Value.Month, input.Value.Day, 0, 0, 0);

            return dt;
        }
        else
            return SqlDateTime.Null;
    }

0

私個人的には、SQL Server 2005(またはそれ以前のバージョン)を扱う場合、ほとんどの場合、これにユーザー定義関数を使用しますが、特にWHERE句に適用する場合(特に、以下を参照)、UDFの使用には特定の欠点があることに注意してください。詳細については、この回答に関するコメント)。SQL Server 2008(またはそれ以上)を使用している場合-以下を参照してください。

実際、私が作成するほとんどのデータベースでは、遅かれ早かれそれらを必要とする可能性が99%あることがわかっているため、これらのUDFを最初のすぐ近くに追加します。

私は「日付のみ」と「時間のみ」用に1つを作成します(「日付のみ」の方がはるかに2つのうち最も使用されていますが)。

以下は、日付に関連するさまざまなUDFへのリンクです。

重要なSQL Serverの日付、時刻、日付時刻関数は
日付のみの関数を取得します

最後のリンクは、日時フィールドの一部のみを取得する3つの方法を示しており、各アプローチの長所と短所について言及しています。

UDFを使用する場合は、クエリのWHERE句の一部としてUDFを使用しないようにする必要があります。これは、クエリのパフォーマンスを大幅に低下させるためです。この主な理由は、WHERE句でUDFを使用すると、その句が検索できないものになるためです。。つまり、SQL Serverは、クエリの実行速度を向上させるために、その句を含むインデックスを使用できなくなります。私自身のUDFの使用法を参照して、WHERE句内で「生の」日付列を頻繁に使用しますが、UDFをSELECTされた列に適用します。このように、UDFはフィルターされた結果セットにのみ適用され、フィルターの一部としてテーブルのすべての行には適用されません。

もちろん、SQL Serverデータベースエンジンは個々の日付と時刻のコンポーネントをネイティブで提供し、これらを個別に効率的にクエリできるため、SQL Server 2008(またはそれ以上)を使用して日付と時刻を分離することが絶対的に最善のアプローチです。複合日時型から日付部分または時刻部分を抽出するためのUDFまたはその他のメカニズムは必要ありません。


UDFの使用は、状況によっては(パラメーターをスクラブするときなど)良い場合があります。しかし、ほとんどの場合、これはひどい解決策です。UDFを行ごとに1回実行することは、クエリのパフォーマンスを無効にする方法であり、クエリを実行する必要はありません。
ErikE 2013年

@ErikE-私はそうは思わない、エリック、UDFはパフォーマンスキラーであるため、SQL Server 2008以降を使用でき、これを行う組み込みのデータ型を使用できる場合、それが最善の解決策となる(必要なものの達成とパフォーマンスの両方の観点から)。これをネイティブでサポートしていない古いバージョンのSQL Serverを使用している場合は、要件を達成するために何かをあきらめることになります。
CraigTP 2013年

本当です。データベースエンジンがSARG可能であるが表現しやすいものを提供してくれるといいですね。それまでの間、一日中いつでも値を探している場合、これは(少なくとも古いバージョンのSQLの場合)依然として最良の解決策ですWHERE DateColumn >= {TimeTruncatingExpression}(@DateValue) AND DateColumn < {TimeTruncatingExpression}(@DateValue + 1)。「ほとんどいつもUDFを使用している」と言っていたので、欠点も説明されておらず、日付のみのクエリをSARG可能にする方法も説明されていなかったので、私は何か言わなければならないと感じました。
ErikE 2013年

@ErikE-心配ありません、エリック。UDFを使用したときは、パフォーマンスが最優先ではない小さなデータセットを使用していたか、クエリを "生の"日付フィールドに対してフィルタリングして(検索可能性を確保するため)、列を選択している可能性があります。 UDFが適用された状態。これらは通常、一度フィルタリングされた小さなデータセットであるため、この少数のレコードに対してUDFを実行しても、それほどパフォーマンスに影響はありません。そうは言っても、あなたは非常に良い点を提起し、私はこれを反映するために私の答えを更新しました。
CraigTP 2013年

-4

私は使うだろう:

CAST
(
CAST(YEAR(DATEFIELD) as varchar(4)) + '/' CAST(MM(DATEFIELD) as varchar(2)) + '/' CAST(DD(DATEFIELD) as varchar(2)) as datetime
) 

したがって、既存の日付フィールドから新しいフィールドを効果的に作成します。


2
どうしてそうするか?datetime値からビットを抽出し、それらを文字列に変換し、それらを連結し、最終的に結果を元に戻す変換はdatetime、たとえば元のdatetimeDATEADD/ DATEDIFFメソッド)で直接計算を実行するよりも良いと思いますか?
Andriy M

また、何をしているMMDD?SQL Serverにはそのような関数はありません。
Andriy M
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.