2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Guid.CreateVersion7() で生成した UUIDv7 は SQL Server で期待通りに使えない

2
Last updated at Posted at 2026-04-10

はじめに

UUIDv4 を主キーに使っている SQL Server のテーブルでは、クラスタードインデックスの断片化が問題になりがちです。UUIDv4 は完全ランダムなので、INSERT のたびにインデックスの中間にページ分割が発生します。

一方、.NET 9.0 では UUIDv7 を生成する Guid.CreateVersion7() が追加されました。UUIDv7 はタイムスタンプベースで時系列順にソートできるため、これを使えば断片化を解決できるのではないか。そう考えて調べたところ、SQL Server の uniqueidentifier 型では UUIDv7 の順序性が機能しないことがわかりました。

この記事では、その原因と現時点での選択肢を整理します。

UUIDv7 とは

UUIDv7 は RFC 9562 で定義された UUID のバージョンです。従来の UUIDv4(完全ランダム)とは異なり、先頭 48 ビットに Unix Epoch からのミリ秒タイムスタンプを持ちます。

ビット範囲 サイズ 内容
0-47 48bit Unix Epoch からのミリ秒タイムスタンプ
48-51 4bit バージョン(固定値 0111
52-63 12bit ランダム
64-65 2bit バリアント(固定値 10
66-127 62bit ランダム

先頭にタイムスタンプがあるため、生成順序がそのままバイト列の大小関係と一致します。

.NET 9.0 での UUIDv7 サポート

.NET 9.0 で Guid.CreateVersion7() メソッドが追加されました。

// UUIDv7 を生成
Guid id = Guid.CreateVersion7();

// タイムスタンプを指定して生成することも可能
Guid idWithTimestamp = Guid.CreateVersion7(DateTimeOffset.UtcNow);

従来の Guid.NewGuid() は UUIDv4(完全ランダム)を生成するのに対し、CreateVersion7() は時系列順序性を持つ UUID を生成します。主キーやクラスタードインデックスに使えば、INSERT のたびにインデックス末尾に追記される形になり、ページ分割が発生しにくくなります。

Entity Framework Core などの ORM でエンティティの ID を Guid.NewGuid() から Guid.CreateVersion7() に差し替えるだけで、インデックス効率の向上が期待できます。

ただし、この期待が成り立つかどうかは、データベース側の UUID の扱いに依存します。

SQL Server の uniqueidentifier 型の問題

バイト順序の不一致

SQL Server の uniqueidentifier 型は、RFC 標準とは異なる独自のバイト順序で値を格納・比較します。

UUIDv7 では、先頭 6 バイト(バイト 0-5)がタイムスタンプです。しかし SQL Server はこれをそのまま格納しません。

バイト位置 UUIDv7 での役割 SQL Server の格納順
0-3 タイムスタンプ(上位) 逆順(3, 2, 1, 0)
4-5 タイムスタンプ(下位) スワップ(5, 4)
6-7 バージョン + ランダム スワップ(7, 6)
8-15 ランダム そのまま

先頭 4 バイトはリトルエンディアンで逆順に格納され、バイト 4-5 とバイト 6-7 もそれぞれスワップされます。バイト 8-15 のみが元の順序を保ちます。

ソート順序の問題

バイト順序の問題に加え、SQL Server は uniqueidentifier のソート時にも独自のルールを持っています。

SQL Server は GUID を 5 つのバイトグループ(4-2-2-2-6 バイト)に分割し、最後の 6 バイトグループから順に比較します。つまり、末尾のバイトが最も優先されます。

UUIDv7 のタイムスタンプは先頭 48 ビット(バイト 0-5)に格納されていますが、SQL Server のソートではこの部分の優先度が最も低くなります。結果として、UUIDv7 を uniqueidentifier 型に格納して ORDER BY しても、時系列順には並びません。

// C# で異なる時刻の UUIDv7 を3つ生成
var id1 = Guid.CreateVersion7(new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero));
var id2 = Guid.CreateVersion7(new DateTimeOffset(2024, 6, 1, 0, 0, 0, TimeSpan.Zero));
var id3 = Guid.CreateVersion7(new DateTimeOffset(2024, 12, 1, 0, 0, 0, TimeSpan.Zero));
-- SQL Server で ORDER BY すると...
SELECT Id, CreatedAt FROM MyTable ORDER BY Id;

-- 結果: 生成順(id1 → id2 → id3)とは一致しない並び順になる
-- ランダム部分(バイト 8-15)がソートで優先されるため

この挙動は NEWID() で生成した UUIDv4 をソートした場合と実質的に変わりません。UUIDv7 の最大の利点である時系列順序性が、SQL Server では機能しないということです。

他の DB との比較

同じ UUIDv7 でも、データベースによって扱いが異なります。

データベース UUID のバイト順序 UUIDv7 の時系列ソート
PostgreSQL RFC 標準(ビッグエンディアン) そのまま機能する
MySQL UUID_TO_BIN(uuid, 1) でスワップ可能 スワップフラグで対応可能
SQL Server 独自の混合エンディアン 機能しない

他の主要 DB では UUIDv7 の順序性を活かせる仕組みがありますが、SQL Server にはこれらに相当する仕組みがありません。

現時点での選択肢

SQL Server で順序性のある ID を使いたい場合、以下の選択肢があります。

1. NEWSEQUENTIALID() を使う

SQL Server がネイティブに提供する順序付き UUID 生成関数です。

CREATE TABLE MyTable (
    Id uniqueidentifier DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
    Name nvarchar(100)
);

前回生成した値より大きい GUID を生成するため、クラスタードインデックスの断片化を抑えられます。ただし、以下の制約があります。

  • DEFAULT 制約でのみ使用可能(SELECT NEWSEQUENTIALID() のような直接呼び出しは不可)
  • ID の生成がデータベースサーバー側に限定される。Guid.NewGuid() でアプリケーション側から ID を生成していた設計からの移行では、INSERT の仕方を変更する必要がある
  • フェイルオーバー時に順序性がリセットされる可能性がある

2. binary(16) 型で格納する

uniqueidentifier 型を避け、binary(16) 型に UUIDv7 のバイト列をそのまま格納する方法です。

CREATE TABLE MyTable (
    Id binary(16) PRIMARY KEY,
    Name nvarchar(100)
);

binary 型はバイト列の先頭から素直に比較されるため、UUIDv7 の時系列順序性が保たれます。ただし、uniqueidentifier 型と比べて以下のトレードオフがあります。

  • NEWID()NEWSEQUENTIALID() と併用できない
  • SSMS などのツールでの表示が 16 進数のバイト列になり、可読性が下がる
  • ORM のマッピングに追加の設定が必要になる場合がある

まとめ

  • .NET 9.0 の Guid.CreateVersion7() で UUIDv7 を生成できるようになったが、SQL Server の uniqueidentifier 型に格納すると時系列順ソートは機能しない
  • 原因は SQL Server 独自のバイト順序(混合エンディアン)とソート優先順位にある
  • SQL Server で UUID の順序性が必要な場合は、NEWSEQUENTIALID() または binary(16) 型を要件に応じて選択する

参考

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?