SQL Serverで datetime や datetime2 の列を扱っていると、「時刻はいらないので日付だけ表示したい」「2026-05-09 のデータだけ検索したい」という場面がよくあります。
結論からいうと、表示や取得だけなら CONVERT(date, 日時列) または CAST(日時列 AS date) を使います。一方、WHERE 句で日付だけを比較する場合は、列に関数をかけずに >= 開始日時 AND < 翌日 の範囲条件にするのが基本です。
SQL Serverで日付のみを取り出すなら
CONVERT(date, created_at) か CAST(created_at AS date) です。ただし検索条件では CONVERT(date, created_at) = '2026-05-09' のように列へ関数をかけると、インデックスを活かしにくくなります。実務では created_at >= '2026-05-09' AND created_at < '2026-05-10' の形を優先します。まず使うSQL
急いでいる場合は、次の形をそのまま使えます。表示だけなら CONVERT(date, 列)、検索条件なら開始日以上・翌日未満です。
-- 日付のみを表示する
SELECT
order_id,
CONVERT(date, ordered_at) AS order_date
FROM dbo.Orders;
-- 特定日のデータを取得する
DECLARE @target_date date = '2026-05-09';
SELECT *
FROM dbo.Orders
WHERE ordered_at >= @target_date
AND ordered_at < DATEADD(day, 1, @target_date);
この書き方なら、ordered_at に時刻が入っていてもその日全体を対象にできます。また、列側を変換しないため、ordered_at のインデックスを使いやすい形になります。
日付のみを取得する基本形
datetime 列から日付部分だけを取得する基本形は次の2つです。
SELECT
order_id,
ordered_at,
CONVERT(date, ordered_at) AS order_date_1,
CAST(ordered_at AS date) AS order_date_2
FROM dbo.Orders;
CONVERT(date, ordered_at) も CAST(ordered_at AS date) も、結果は date 型になります。時刻部分を文字列として削っているわけではなく、SQL Serverのデータ型変換として日付型へ変換しています。
| 書き方 | 戻り値 | 使いどころ |
|---|---|---|
CONVERT(date, ordered_at) |
date |
SQL Serverらしい書き方。日付変換だと意図が読み取りやすい |
CAST(ordered_at AS date) |
date |
標準SQL寄りの書き方。単純な型変換として読みやすい |
FORMAT(ordered_at, 'yyyy-MM-dd') |
nvarchar |
画面表示用。検索条件や集計キーには基本的に使わない |
どちらかで迷う場合、SQL Serverの記事や既存SQLで CONVERT が多い環境なら CONVERT(date, ordered_at)、標準的な型変換として統一したいなら CAST(ordered_at AS date) でよいです。
WHERE句で日付のみ比較するなら範囲条件にする
検索条件で「特定日のデータ」を取りたい場合、次のように列へ関数をかける書き方は避けたいです。
-- 動きますが、ordered_at列のインデックスを活かしにくい SELECT * FROM dbo.Orders WHERE CONVERT(date, ordered_at) = '2026-05-09';
この条件は読みやすいものの、ordered_at の値を1行ずつ日付型へ変換してから比較する形になります。対象列にインデックスがあっても、範囲探索として使いにくくなることがあります。
実務では、開始日時以上かつ翌日未満の範囲条件にします。
-- 2026-05-09 の 00:00:00 以上、2026-05-10 の 00:00:00 未満 SELECT * FROM dbo.Orders WHERE ordered_at >= '2026-05-09' AND ordered_at < '2026-05-10';
この形なら ordered_at 列をそのまま比較できるため、インデックスを使った範囲検索にしやすくなります。日付範囲の考え方は、既存の SQLの日付範囲指定 でも詳しく整理しています。
= ‘YYYY-MM-DD’ では時刻付きデータを拾えない
datetime 列に対して、次のように = '2026-05-09' と書くと、2026-05-09 00:00:00 と完全一致する行だけが対象になります。
-- ordered_at が 2026-05-09 10:30:00 の行は一致しない SELECT * FROM dbo.Orders WHERE ordered_at = '2026-05-09';
日中に登録されたデータは 2026-05-09 10:30:00 や 2026-05-09 23:59:59.997 のように時刻を持っています。そのため「その日すべて」を取りたいなら、完全一致ではなく範囲条件で書きます。
DECLARE @target_date date = '2026-05-09'; SELECT * FROM dbo.Orders WHERE ordered_at >= @target_date AND ordered_at < DATEADD(day, 1, @target_date);
固定の日付リテラルではなく変数で受ける場合も、DATEADD(day, 1, @target_date) で翌日を作ると安全です。時刻の最終値を 23:59:59 のように手で書くより、翌日未満にしたほうが datetime2 の細かい精度にも対応しやすくなります。
CONVERT(date)とCASTの違い
datetime から date へ変換するだけなら、CONVERT と CAST の結果はほぼ同じです。大きな違いは、CONVERT には第3引数のスタイル指定があることです。
DECLARE @dt datetime = '2026-05-09T14:35:20.123';
SELECT
CONVERT(date, @dt) AS converted_date,
CAST(@dt AS date) AS casted_date,
CONVERT(varchar(10), @dt, 23) AS formatted_text;
| 目的 | おすすめ | 理由 |
|---|---|---|
| 日付型として取得する | CONVERT(date, 列) または CAST(列 AS date) |
戻り値が date なので、後続の比較や集計で扱いやすい |
| 文字列として yyyy-mm-dd で表示する | CONVERT(varchar(10), 列, 23) |
スタイル23は日付だけのISO形式として読みやすい |
| ロケール付きの表示にする | FORMAT |
表示用としては便利。ただし戻り値は文字列で、処理コストにも注意 |
検索や結合、集計のキーとして使うなら、文字列化より date 型で扱うほうが素直です。FORMAT は表示目的に寄せた関数なので、データ抽出の条件には向きません。
FORMATは表示用に限定する
FORMAT は yyyy/MM/dd や日本語カルチャを意識した表示には便利です。ただし戻り値は文字列で、SQL Serverの公式ドキュメントでも一般的なデータ型変換には CAST や CONVERT を使う説明になっています。
SELECT
order_id,
ordered_at,
FORMAT(ordered_at, 'yyyy/MM/dd', 'ja-JP') AS display_date
FROM dbo.Orders;
-- 検索条件では避ける
SELECT *
FROM dbo.Orders
WHERE FORMAT(ordered_at, 'yyyy-MM-dd') = '2026-05-09';
検索条件で FORMAT を使うと、列を文字列に変換して比較するため、インデックスを使った効率的な検索から遠ざかります。表示はアプリ側で整形するか、SQLで必要な場合もSELECT句だけに留めるのが無難です。
日別集計で使う場合
日別の件数や売上を出す場合は、SELECT と GROUP BY の両方で日付部分に変換します。
SELECT
CONVERT(date, ordered_at) AS order_date,
COUNT(*) AS order_count,
SUM(total_amount) AS total_amount
FROM dbo.Orders
WHERE ordered_at >= '2026-05-01'
AND ordered_at < '2026-06-01'
GROUP BY CONVERT(date, ordered_at)
ORDER BY order_date;
ここで重要なのは、WHERE 句では範囲条件を使い、GROUP BY で日付単位に丸めることです。全期間を対象にしてから日付変換するより、先に対象期間を絞ったほうが余計な読み込みを減らせます。
SELECT
DATEFROMPARTS(YEAR(ordered_at), MONTH(ordered_at), 1) AS order_month,
COUNT(*) AS order_count,
SUM(total_amount) AS total_amount
FROM dbo.Orders
WHERE ordered_at >= '2026-01-01'
AND ordered_at < '2027-01-01'
GROUP BY DATEFROMPARTS(YEAR(ordered_at), MONTH(ordered_at), 1)
ORDER BY order_month;
日付の比較条件そのものを整理したい場合は、SQLの日付比較 もあわせて確認すると、前後関係や相対期間の考え方を整理しやすくなります。
インデックスを効かせたいときの考え方
SQL Serverで日付検索が遅い場合、まず確認したいのは「列を加工してから比較していないか」です。次のような条件は読みやすい反面、列に関数をかけています。
WHERE CONVERT(date, ordered_at) = '2026-05-09' WHERE CAST(ordered_at AS date) = '2026-05-09' WHERE FORMAT(ordered_at, 'yyyy-MM-dd') = '2026-05-09'
インデックスを活かしたいなら、列はそのまま左辺に置き、右辺で範囲を作ります。
DECLARE @from datetime2 = '2026-05-09T00:00:00'; DECLARE @to datetime2 = '2026-05-10T00:00:00'; SELECT * FROM dbo.Orders WHERE ordered_at >= @from AND ordered_at < @to;
この形はSARGableな条件にしやすく、ordered_at のインデックスがある場合に範囲探索として扱われやすくなります。SQL Serverで遅いSQLを追うときは、実行計画と統計情報も一緒に見ると原因を切り分けやすいです。SQL Serverの統計情報を更新する方法 も関連します。
毎回日付だけで検索するなら計算列も検討する
大量データのテーブルで、業務上ほぼ毎回「日付だけ」で検索するなら、日付部分を計算列として持たせて、その列にインデックスを作る方法もあります。全てのケースで必要ではありませんが、同じ条件が繰り返し遅い場合の選択肢になります。
ALTER TABLE dbo.Orders ADD ordered_date AS CONVERT(date, ordered_at) PERSISTED; CREATE INDEX IX_Orders_ordered_date ON dbo.Orders (ordered_date); SELECT * FROM dbo.Orders WHERE ordered_date = '2026-05-09';
ただし、計算列とインデックスを増やすと、INSERTやUPDATE時の更新コストも増えます。まずは範囲条件で十分かを確認し、それでも日付単位の検索が多く遅い場合に検討するのが現実的です。
BETWEENを使う場合の注意
BETWEEN は両端を含む条件です。そのため、datetime や datetime2 の列で日付を扱う場合、終端をどう書くかで漏れや重複が起きやすくなります。
-- 2026-05-09 23:59:59.500 などがあると漏れる可能性がある SELECT * FROM dbo.Orders WHERE ordered_at BETWEEN '2026-05-09' AND '2026-05-09 23:59:59';
日付範囲では、終端を含めるより「翌日未満」にしたほうが安全です。
SELECT * FROM dbo.Orders WHERE ordered_at >= '2026-05-09' AND ordered_at < '2026-05-10';
月単位でも同じです。5月分を取得するなら、2026-05-01 以上、2026-06-01 未満にします。
SELECT * FROM dbo.Orders WHERE ordered_at >= '2026-05-01' AND ordered_at < '2026-06-01';
今日・昨日・今月のデータを取得する
固定日ではなく、実行時点を基準に「今日」「昨日」「今月」を取りたい場合も、列に関数をかけず、右辺で期間の開始・終了を作ります。
-- 今日のデータ DECLARE @today date = CONVERT(date, GETDATE()); SELECT * FROM dbo.Orders WHERE ordered_at >= @today AND ordered_at < DATEADD(day, 1, @today); -- 昨日のデータ SELECT * FROM dbo.Orders WHERE ordered_at >= DATEADD(day, -1, @today) AND ordered_at < @today; -- 今月のデータ DECLARE @month_start date = DATEFROMPARTS(YEAR(@today), MONTH(@today), 1); SELECT * FROM dbo.Orders WHERE ordered_at >= @month_start AND ordered_at < DATEADD(month, 1, @month_start);
GETDATE() は現在日時を返すため、そのまま比較すると時刻も含まれます。日単位・月単位で比較したい場合は、まず基準日の開始を作ってから、終了側を DATEADD で作ると安全です。
よくある使い分け
| やりたいこと | 書き方 | 補足 |
|---|---|---|
| SELECT結果に日付だけ表示したい | CONVERT(date, ordered_at) |
戻り値は date。表示整形はアプリ側でもよい |
| 特定日のデータを検索したい | ordered_at >= @d AND ordered_at < DATEADD(day, 1, @d) |
インデックスを使いやすい |
| 日別に集計したい | GROUP BY CONVERT(date, ordered_at) |
WHEREで期間を先に絞る |
| 文字列で yyyy-mm-dd 表示したい | CONVERT(varchar(10), ordered_at, 23) |
表示目的。検索条件には使わない |
| 日本語や文化圏込みで表示したい | FORMAT |
便利だが表示用に限定する |
よくある質問
CONVERT(date)とCASTはどちらを使えばよいですか?
日付型へ変換するだけならどちらでも構いません。SQL Serverらしい書き方として CONVERT(date, 列) を使う現場も多いです。標準SQL寄りにしたい場合は CAST(列 AS date) でも問題ありません。
WHERE句でCONVERT(date, 列)を使ってはいけませんか?
少量データや一時調査では使っても動きます。ただし本番の検索条件では、列に関数をかけることでインデックスを活かしにくくなることがあります。件数が多いテーブルでは範囲条件にするのがおすすめです。
datetimeとdatetime2では書き方は変わりますか?
基本の考え方は同じです。datetime2 は小数秒の精度が高いため、終端を 23:59:59 のように書くより、< 翌日 の形にしたほうが安全です。
日付を文字列で比較してもよいですか?
日付リテラルとして解釈される形なら動きますが、列を文字列化して比較するのは避けます。検索条件では日時型のまま範囲比較し、表示が必要なときだけ文字列化するのが扱いやすいです。
まとめ
SQL Serverで datetime から日付のみを取得するなら、CONVERT(date, 日時列) または CAST(日時列 AS date) を使います。表示だけならこれで十分です。
ただし、特定日のデータを検索する WHERE 句では、CONVERT(date, 日時列) = 'YYYY-MM-DD' ではなく、日時列 >= 'YYYY-MM-DD' AND 日時列 < '翌日' の範囲条件を優先します。この形なら時刻付きデータを漏らしにくく、インデックスも活かしやすくなります。

