【SQLServer】datetimeを分割して取得する



# 目次

[1. やりたいこと](#1やりたいこと)
[2. 環境](#2環境)
[3. 調査](#3調査)
　[3-1. 日付のみ](#3-1日付のみ)
　　[(1)LEFT関数](#1left関数)
　　[(2)SUBSTRING関数](#2substring関数)
　　[(3)CAST関数](#3cast関数)
　　[(4)CONVERT関数](#4convert関数)
　　[(5)おまけ：スラッシュにしたい](#5おまけスラッシュにしたい)
　　[(6)補足：CAST関数とCONVERT関数の違い](#6補足cast関数とconvert関数の違い)
　[3-2. 時刻のみ](#3-2時刻のみ)
[4. 最終的なコード](#4最終的なコード)
[5. 参考](#5参考)
[6. 最後に](#6最後に)

# 1.やりたいこと

SQLServerのとあるテーブルに、日時というカラムがあります。

データ型はdatetimeです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/24190e8e-94ec-9255-95c1-6ccd8f497f29.png" width=50%>

datetimeのフォーマットは以下です。
`YYYY-MM-DD hh:mm:ss[.nnn]`

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/fb4255e1-f193-e277-32c7-b8740f7a937b.png" width=50%>

今回は~~もともとあるデータを何とか使って~~日付と時刻を分けたかったので、調査してまとめました。
例：`2022-10-04 6:56:24`→`2022-10-04` と `06:56:24` に分ける。


# 2.環境

- windows10 バージョン21H2
- Microsoft-IIS 10.0
- Microsoft SQLServer Management Studio 18.12.1


# 3.調査

いろんな関数を使ってどのような結果になるのか調査しました。


## 3-1.日付のみ

### (1)LEFT関数

ハイフンを含めた先頭10文字を指定することで、`2022-10-04`と取得ができないかと思い試しました。

＜実行したSQL＞

```sql
SELECT
id,
name,
LEFT(time, 10) AS '日付'
FROM test_DB
```

＜結果＞
`10 04 2022`が取得できました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/6797251b-1b6e-1fe9-70aa-206beef3e715.png" width=40%>

### (2)SUBSTRING関数

指定した文字列を切り取って返す関数です。SQLServerとMySQLで使うことができるため、試しました。

:::note info
**SUBSTRING関数**
SUBSTRING([文字列], [切り取り開始地点], [切り取る文字数]);
- 第一引数：文字列
- 第二引数：切り取りの開始地点
- 第三引数：切り取る文字数
:::

＜実行したSQL＞

```sql
SELECT
id,
name,
SUBSTRING(time, 1, 10) AS '日付'
FROM test_DB
```

＜結果＞
「引数の数が対応していない」旨のエラーが表示されました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/6416e77c-c4e5-1618-0069-8ee1ab62b09d.png" width=70%>

第一引数は文字列とあるので、一度文字列に変換する必要があります。

CAST関数で文字列に変換し、第一引数とすることでエラーが解消できました。

:::note info
**CAST関数**
CAST ( 任意の有効な式 AS 対象のデータ型 [ ( データ型の長さ ) ] )
あるデータ型の式を別のデータ型に変換します。
:::

＜実行したSQL＞
```sql
SELECT
id,
name,
SUBSTRING(CAST(time AS nvarchar), 1, 10) AS '日付'
FROM test_DB
```
＜CAST関数を使った後の結果＞
`10 04 2022`が取得でき、LEFT関数と同じ結果になりました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/8fe153ae-f7a8-3c22-d986-7f5006208279.png" width=40%>

### (3)CAST関数

(2)でいったん文字列に変換することが手間だと感じたので、「CAST関数で一回で取得できないか？」と考えました。

＜実行したSQL＞
```sql
SELECT
id,
name,
(CAST(time AS date)) AS '日付'
FROM test_DB
```
(2)のSUBSTRING関数では文字列に変換しましたが、今回はdateを指定しました。

＜結果＞
dateの既定の形式が`YYYY-MM-DD`なので、`2022-10-04`と取得することができました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/071b9474-352a-b2fc-e16b-070067633deb.png" width=40%>

### (4)CONVERT関数

CAST関数と同様で、あるデータ型の式を別のデータ型に変換する関数として
CONVERT関数があります。
CONVERT関数でも取得できるか、試してみました。

:::note info
**CONVERT関数**
CONVERT ( ( 対象のデータ型 [ ( データ型の長さ ) ] ) , 任意の式 [ , 任意の式を変換する方法を指定する整数式 ] )
:::

＜実行したSQL＞
```sql
SELECT
id,
name,
(CONVERT(date, time)) AS '日付'
FROM test_DB
```
＜結果＞
CAST関数と同様で、`2022-10-04`と取得することができました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/6a6dcb8f-edd6-0136-0ebf-e05957b7d84c.png" width=40%>


### (5)おまけ：スラッシュにしたい

datetime型のデータから日付のみ取得したいという目的は達成できたのですが、`2022-10-04`ではなく、`2022/10/04`というように「スラッシュにすることはできるのかな？」と考えました。

＜実行したSQL＞
```sql
SELECT
id,
name,
(CONVERT(nvarchar, time, 111)) AS '日付'
FROM test_DB
```
＜結果＞
CONVERT関数の最後に整数式を指定してできました。

:::note info
**CONVERT関数**
CONVERT ( ( 対象のデータ型 [ ( データ型の長さ ) ] ) , 任意の式  **[ , 任意の式を変換する方法を指定する整数式 ]** )
:::

任意の式の後に111 を入れることで 既定の形式である`yyyy/mm/dd`にすることができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/043f7c2e-6b72-e04b-73a2-39a7a4e3fd7f.png" width=40%>



### (6)補足：CAST関数とCONVERT関数の違い

CONVERT関数はSQLServerの独自機能で、CAST関数はISO に準拠しています。

## 3-2.時刻のみ

時刻については(5)と同様の式で取得することができます。

＜実行したSQL＞
```sql
SELECT
id,
name,
(CONVERT(nvarchar, time, 108)) AS '時間'
FROM test_DB
```
＜結果＞
108 を入れることで 既定の形式である`hh:mi:ss`にすることができます。
114にするとミリ秒（`hh:mi:ss:mmm`）まで取得可能です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/c0a756d2-e0d2-b9b1-7adf-b271c98fd9dd.png" width=40%>


# 4.最終的なコード

CONVERT関数を使用することで日付と時間を分割できました。

```sql
SELECT
id,
name,
(CONVERT(date, time)) AS '日付',
(CONVERT(nvarchar, time, 108)) AS '時間'
FROM test_DB
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689555/e21dba3c-45af-8a01-21f4-295e281ea26b.png" width=50%>


# 5.参考

https://it-kyujin.jp/article/detail/785/

https://www.fenet.jp/dotnet/column/database/sql-server/1403/

https://learn.microsoft.com/ja-jp/sql/t-sql/functions/date-and-time-data-types-and-functions-transact-sql?view=sql-server-ver16

https://www.sejuku.net/blog/73601

https://kaya-soft.com/sqlserver2008-toranomaki/beginner/function/#No17

https://knowledge.reontosanta.com/archives/443

https://www.curict.com/item/d7/d799374.html

https://sql55.com/query/convert-datetime-to-string.php

https://learn.microsoft.com/ja-jp/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16

https://learn.microsoft.com/ja-jp/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver16

https://pg-life.net/db/data-type/

https://learn.microsoft.com/ja-jp/sql/t-sql/data-types/date-transact-sql?view=sql-server-ver16

# 6.最後に

今回は業務で要望があったので、いろいろな関数を調査しました。
間違いやご意見等ありましたらぜひ仰ってください。
読んでいただき、ありがとうございました。