{"id":46589,"date":"2026-09-04T13:30:42","date_gmt":"2026-09-04T11:30:42","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/?p=46589"},"modified":"2026-09-04T13:30:44","modified_gmt":"2026-09-04T11:30:44","slug":"why-uuidv7-does-not-reduce-fragmentation-in-sql-server","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/","title":{"rendered":"Why UUIDv7 does not reduce fragmentation in SQL Server"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">This post came out of the <a href=\"https:\/\/eightkb.online\/\">8kb conference<\/a>. <a href=\"https:\/\/www.linkedin.com\/in\/ACoAAABn0aoBfhQG_7FzQSGQew1NKQe1l6E95Ao?miniProfileUrn=urn%3Ali%3Afs_miniProfile%3AACoAAABn0aoBfhQG_7FzQSGQew1NKQe1l6E95Ao&amp;lipi=urn%3Ali%3Apage%3Ad_flagship3_curation_hub_pf_followers%3B2410dnnZRhCNii8L7k1EQw%3D%3D\">Andy Yun&#8217;s<\/a> session pointed to <a href=\"https:\/\/www.youtube.com\/watch?v=JYYHalwjBwA\">Jeff Moden&#8217;s <em>Black Arts Index Maintenance<\/em><\/a>, a talk that argues fragmentation percentages are the wrong thing to watch and that page density is where the real cost sits. Watching it left me with a question it does not answer: if density is what matters, what happens when the clustering key is a GUID, and does UUIDv7 improve the picture the way it does elsewhere?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The answer turns on how SQL Server compares <code>uniqueidentifier<\/code> values, which is not explicitly documented. So it has to be measured.<\/p>\n\n\n\n<h2 id=\"h-context\" class=\"wp-block-heading\">Context<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To showcase it, run this on any instance. No database required.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSELECT g\nFROM (VALUES\n    (CAST(&#039;ffffffff-ffff-ffff-ffff-000000000000&#039; AS uniqueidentifier)),\n    (CAST(&#039;00000000-0000-0000-0000-000000000001&#039; AS uniqueidentifier))\n) v(g)\nORDER BY g;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The value starting with&nbsp;<code>ffffffff<\/code>&nbsp;comes back first, ahead of the one that is almost entirely zeros.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That behaviour is intentional, and Microsoft covers it <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/t-sql\/data-types\/uniqueidentifier-transact-sql\">in a sentence<\/a> that is easy to overlook: for&nbsp;<code>uniqueidentifier<\/code>, <span style=\"text-decoration: underline\">ordering is not implemented by comparing the bit patterns of the two values<\/span>. The documentation stops there. It tells you the comparison is something other than a plain byte scan, then leaves the actual sequence unstated.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The omission has become expensive. Developers are now bringing UUIDv7, the time-ordered identifier standardised in <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc9562.html\">RFC 9562<\/a>, on the reasonable assumption that a leading timestamp turns scattered inserts into an append pattern. That holds where a database compares UUIDs lexicographically over the RFC byte sequence. SQL Server uses a different order, and nothing about the identifier warns you.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Since the order is undocumented, the only way to establish it is to measure it.<\/p>\n\n\n\n<h2 id=\"h-how-the-bytes-are-stored\" class=\"wp-block-heading\">How the bytes are stored<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/t-sql\/data-types\/uniqueidentifier-transact-sql\">A&nbsp;<code>uniqueidentifier<\/code>&nbsp;is a 16-byte value<\/a>. The five hyphen-separated groups you see on screen are a display convention. Storage follows the Windows GUID structure, which is mixed-endian: the first three groups are byte-reversed on disk, while the last two keep the order you read them in.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSELECT CAST(CAST(&#039;01020304-0506-0708-090a-0b0c0d0e0f10&#039; AS uniqueidentifier) AS binary(16));\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"265\" height=\"43\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-5.png\" alt=\"\" class=\"wp-image-46717\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Every byte here is distinct, so both halves of the rule are visible at once. The first group <code>01020304<\/code> is stored <code>04 03 02 01<\/code>, the second <code>0506<\/code> becomes <code>06 05<\/code>, the third <code>0708<\/code> becomes <code>08 07<\/code>. The fourth and fifth groups appear exactly as written.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>String group<\/th><th>Stored bytes<\/th><th>Reversed<\/th><\/tr><\/thead><tbody><tr><td>1st (8 hex)<\/td><td>0 to 3<\/td><td>yes<\/td><\/tr><tr><td>2nd (4 hex)<\/td><td>4 to 5<\/td><td>yes<\/td><\/tr><tr><td>3rd (4 hex)<\/td><td>6 to 7<\/td><td>yes<\/td><\/tr><tr><td>4th (4 hex)<\/td><td>8 to 9<\/td><td>no<\/td><\/tr><tr><td>5th (12 hex)<\/td><td>10 to 15<\/td><td>no<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 id=\"h-the-comparison-order\" class=\"wp-block-heading\">The comparison order<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The&nbsp;ADO.NET&nbsp;<a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/framework\/data\/adonet\/sql\/comparing-guid-and-uniqueidentifier-values\">documentation <\/a>supplies part of the answer. <code>SqlGuid<\/code>&nbsp;implements&nbsp;<code>CompareTo<\/code>&nbsp;to match SQL Server behaviour, treating the last six bytes as the most significant, whereas&nbsp;<code>System.Guid<\/code>&nbsp;evaluates all sixteen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Testing the rest against a live instance produces a sequence that held for every value I tried:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">SQL Server reorders the sixteen stored bytes as&nbsp;<code>10-15, 8-9, 6-7, 4-5, 0-3<\/code>, then compares them left to right.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">A single permutation followed by an ordinary binary comparison, with no special handling per group.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Applied to the two values above:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nA = ffffffff-ffff-0010-0000-aaaaaaaaaaaa\nB = ffffffff-ffff-2000-0000-aaaaaaaaaaaa\n\nstored     A : FFFFFFFF FFFF 1000 0000 AAAAAAAAAAAA\n           B : FFFFFFFF FFFF 0020 0000 AAAAAAAAAAAA\n\nreordered  A : AAAAAAAAAAAA 0000 1000 FFFF FFFFFFFF\n           B : AAAAAAAAAAAA 0000 0020 FFFF FFFFFFFF\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">The first eight bytes are identical in both keys, the ninth settles the comparison, and the remaining seven are never examined.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"860\" height=\"440\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/08\/guid-read-order.gif\" alt=\"\" class=\"wp-image-46598\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The six bytes read first are stored positions 10 to 15. In an RFC 4122 UUIDv1 those hold the node field, historically <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc9562.html\">derived from the MAC address,<\/a> though implementations are free to substitute a random value there.<\/p>\n\n\n\n<h2 id=\"h-testing-it-on-10-000-values\" class=\"wp-block-heading\">Testing it on 10,000 values<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Hand-picked examples illustrate a rule without showing that it generalises. To check that, rebuild the proposed key in T-SQL and compare the order it predicts against the engine&#8217;s own.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nWITH G AS (\n    SELECT TOP (10000) g = NEWID() FROM sys.all_columns a CROSS JOIN sys.all_columns b\n), B AS (\n    SELECT g, b = CAST(g AS binary(16)) FROM G\n), P AS (\n    SELECT g, Cle = SUBSTRING(b,11,6) + SUBSTRING(b,9,2) + SUBSTRING(b,7,2)\n                  + SUBSTRING(b,5,2)  + SUBSTRING(b,1,4)\n    FROM B\n), R AS (\n    SELECT R1 = ROW_NUMBER() OVER (ORDER BY g),\n           R2 = ROW_NUMBER() OVER (ORDER BY Cle)\n    FROM P\n)\nSELECT Mismatches = SUM(CASE WHEN R1 &lt;&gt; R2 THEN 1 ELSE 0 END) FROM R;\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Zero mismatches on 10,000 values. Substituting the naive key, sixteen bytes read straight through from left to right, returns 10,000 mismatches on the same set, which at least confirms the test discriminates between the two models.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is strong evidence, but it stops short of proof. Repeated runs, with fresh values each time, returned zero mismatches every time. The implementation itself is undocumented, and what I ran covers one version of SQL Server (2022).<\/p>\n\n\n\n<h2 id=\"h-where-uuidv7-puts-its-timestamp\" class=\"wp-block-heading\">Where UUIDv7 puts its timestamp<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">RFC 9562 builds UUIDv7 around a 48-bit big-endian Unix timestamp in milliseconds, placed in the leading bytes. The remaining fields carry version and variant bits along with random data, optionally including a sub-millisecond fraction or a counter to <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc9562.html\">improve monotonicity inside a single millisecond<\/a>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nTTTTTTTT - TTTT - 7RRR - VRRR - RRRRRRRRRRRR\n|_____________|                              48-bit timestamp\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">That timestamp occupies the first two textual groups, so in storage its bytes land in positions 0 to 3 and 4 to 5, both byte-reversed. Positions 4 to 5 are read only after 10 to 15, 8 to 9 and 6 to 7, and positions 0 to 3 come last of all.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Which means the fields SQL Server examines first contain version bits, variant bits and random data. Independently generated values separate on those long before the timestamp is reached, so for a clustered index taking single-row inserts the append behaviour UUIDv7 is known for simply does not materialise.<\/p>\n\n\n\n<h2 id=\"h-measured-on-25-000-inserts\" class=\"wp-block-heading\">Measured on 25,000 inserts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Three tables, identical apart from the clustered key,&nbsp;<code>FILLFACTOR 100<\/code>, 25,000 rows inserted one row per statement. Bulk loading can hide the effect entirely. When SQL Server sets <code>DMLRequestSort<\/code> on a clustered index insert, it feeds the rows in key order, which <a href=\"https:\/\/sqlperformance.com\/2014\/10\/t-sql-queries\/performance-tuning-whole-plan\" data-type=\"link\" data-id=\"https:\/\/sqlperformance.com\/2014\/10\/t-sql-queries\/performance-tuning-whole-plan\">promotes sequential writes and avoids page splitting<\/a>. Whether it does so depends on the estimated row count, the locking hint and a cost decision, so the safest way to reproduce an OLTP pattern is to insert one row per statement.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Clustered key<\/th><th class=\"has-text-align-right\" data-align=\"right\">Pages<\/th><th class=\"has-text-align-right\" data-align=\"right\">Page fullness<\/th><th class=\"has-text-align-right\" data-align=\"right\">Fragmentation<\/th><\/tr><\/thead><tbody><tr><td><code>NEWID()<\/code><\/td><td class=\"has-text-align-right\" data-align=\"right\">560<\/td><td class=\"has-text-align-right\" data-align=\"right\">68.9 %<\/td><td class=\"has-text-align-right\" data-align=\"right\">99.1 %<\/td><\/tr><tr><td>UUIDv7 (RFC 9562)<\/td><td class=\"has-text-align-right\" data-align=\"right\">568<\/td><td class=\"has-text-align-right\" data-align=\"right\">67.9 %<\/td><td class=\"has-text-align-right\" data-align=\"right\">99.1 %<\/td><\/tr><tr><td><code>NEWSEQUENTIALID()<\/code><\/td><td class=\"has-text-align-right\" data-align=\"right\">391<\/td><td class=\"has-text-align-right\" data-align=\"right\">98.7 %<\/td><td class=\"has-text-align-right\" data-align=\"right\">0.5 %<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">About one percentage point separates&nbsp;<code>NEWID()<\/code>&nbsp;from UUIDv7, and the sign changes between runs, so under this workload UUIDv7 buys you nothing at all.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The sequential key holds the same rows in 391 pages rather than 560, roughly 30 % fewer. Since pages are the unit SQL Server reads and caches, a sparse index needs more buffer pool to hold the same rows and more reads to scan them, which puts the cost on memory and I\/O well before it shows up as disk space.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A few things are worth knowing if you reproduce this. The measurements come from the <code><a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/system-dynamic-management-views\/sys-dm-db-index-physical-stats-transact-sql\">sys.dm_db_index_physical_stats<\/a><\/code> function, called in <code>DETAILED<\/code> mode: <code>avg_page_space_used_in_percent<\/code> comes back <code>NULL<\/code> under <code>LIMITED<\/code>, and passing an <code>index_id<\/code> while <code>object_id<\/code> is <code>NULL<\/code> raises an error rather than returning every index. Read page fullness in preference to fragmentation, since a mid-page split leaves both halves partly empty and fullness is the figure that registers it.<\/p>\n\n\n\n<h2 id=\"h-practical-options\" class=\"wp-block-heading\">Practical options<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The costly combination here is quite specific: a GUID that is random under SQL Server&#8217;s comparison order, used as the clustering key, fed by sustained single-row inserts. Change any one of those three and the physical consequences shift. GUIDs still earn their place whenever identity has to be generated away from the database, by clients, by distributed services, or by merge replication, which relies on&nbsp;<code>uniqueidentifier<\/code>&nbsp;<a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/t-sql\/data-types\/uniqueidentifier-transact-sql\">to keep rows distinct across copies<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A sequential clustered key sends every insert to the same last page, and under concurrency those threads all queue on the same <code>PAGELATCH_EX<\/code>. <a href=\"https:\/\/techcommunity.microsoft.com\/blog\/sqlserver\/behind-the-scenes-on-optimize-for-sequential-key\/806888\">Microsoft describes what happens next<\/a>: the insert that triggers a new page holds the latch longer than usual, the queue builds up behind it, and throughput falls off a cliff. <a href=\"https:\/\/techcommunity.microsoft.com\/blog\/sqlserver\/behind-the-scenes-on-optimize-for-sequential-key\/806888\"><code>OPTIMIZE_FOR_SEQUENTIAL_KEY<\/code> <\/a>caps how many threads may queue for the latch, which keeps throughput steadier without removing the contention. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The useful question, then, is where the GUID sits in the physical design and which of those two costs your workload actually pays.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can move it off the clustering key while leaving it as the primary key. <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/tables\/create-primary-keys?view=sql-server-ver17\" data-type=\"link\" data-id=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/tables\/create-primary-keys?view=sql-server-ver17\">SQL Server lets you declare<\/a> <code>PRIMARY KEY NONCLUSTERED<\/code> on the GUID and cluster on a narrow <code>IDENTITY<\/code> column instead. The physical result matches a clustered <code>IDENTITY<\/code> with a unique constraint on the GUID, but the declaration matches the logical model: the GUID identifies the row, the <code>IDENTITY<\/code> only orders it on disk.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nCREATE TABLE dbo.T (\n    RowId bigint           IDENTITY NOT NULL,\n    Id    uniqueidentifier NOT NULL DEFAULT NEWID(),\n    CONSTRAINT PK_T PRIMARY KEY NONCLUSTERED (Id),\n    INDEX CX_T CLUSTERED (RowId)\n);\n<\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Either way the table stays dense and the 16-byte cost is confined to one index. The question is also narrower than it looks, since it applies to OLTP tables taking single-row inserts. On the analytical side a clustered columnstore index changes the storage model entirely, page density in the rowstore sense stops being the right measure, and this is a typical solution for some Data Warehouse use-cases (thanks to <a href=\"https:\/\/www.linkedin.com\/in\/uwericken\/\" data-type=\"link\" data-id=\"https:\/\/www.linkedin.com\/in\/uwericken\/\">Uwe Ricken<\/a> for the reminder \ud83d\ude09).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can make it sequential instead.&nbsp;<code><a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/t-sql\/functions\/newsequentialid-transact-sql\">NEWSEQUENTIALID()<\/a><\/code>&nbsp;works because it puts the increasing part where SQL Server reads first. Its documented limits are worth reading before you commit: the values are guessable and unsuitable where privacy matters, it only functions as a column&nbsp;<code>DEFAULT<\/code>, and the sequence can restart from a lower range after a Windows restart.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Or accept the cost. That is easy on a small table with rare inserts, where a single rebuild holds and the wasted space is trivial anyway, and harder on a busy random-key table that drifts back to the same density (or higher) after every rebuild.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Another solution could be a <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/indexes\/heaps-tables-without-clustered-indexes?view=sql-server-ver17\" data-type=\"link\" data-id=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/indexes\/heaps-tables-without-clustered-indexes?view=sql-server-ver17\">heap<\/a>, because it removes the ordering question from the table itself: rows land wherever free space allows, and a nonclustered index on the GUID locates them by RID rather than by the clustering key. It suits tables with little DML. An update that grows a row past its page leaves a forwarding record behind, and the RID then costs an extra read on every lookup that follows it. Deletes have a different drawback: the rows are marked as ghosted, but the emptied pages stay allocated to the table unless the delete takes a table lock or the heap is rebuilt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Whichever way you go, audit what is already in production. A large&nbsp;<code>uniqueidentifier<\/code>-keyed index sitting near 69 % fullness deserves a look, since it may be taking values whose insertion order is effectively random for SQL Server, whether they come from&nbsp;<code>NEWID()<\/code>, UUIDv7 or something else. Density only points somewhere. Check fill factor, insert pattern and rebuild history before drawing a conclusion.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\nSELECT\n    &#x5B;Schema]      = OBJECT_SCHEMA_NAME(ps.object_id),\n    &#x5B;Table]       = OBJECT_NAME(ps.object_id),\n    &#x5B;Index]       = i.name,\n    &#x5B;FillFactor]  = NULLIF(i.fill_factor, 0),\n    Pages         = ps.page_count,\n    Fullness      = CAST(ps.avg_page_space_used_in_percent AS decimal(5,1)),\n    Fragmentation = CAST(ps.avg_fragmentation_in_percent  AS decimal(5,1))\nFROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, &#039;SAMPLED&#039;) ps\nJOIN sys.indexes i ON i.object_id = ps.object_id AND i.index_id = ps.index_id\nJOIN sys.index_columns ic ON ic.object_id = i.object_id\n                         AND ic.index_id  = i.index_id\n                         AND ic.key_ordinal = 1\nJOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id\nWHERE ps.index_level = 0\n  AND ps.alloc_unit_type_desc = &#039;IN_ROW_DATA&#039;\n  AND ps.page_count &gt; 500\n  AND i.type IN (1, 2)\n  AND TYPE_NAME(c.system_type_id) = &#039;uniqueidentifier&#039;\nORDER BY ps.avg_page_space_used_in_percent;\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"538\" height=\"114\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/08\/image-79.png\" alt=\"\" class=\"wp-image-46603\" srcset=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/08\/image-79.png 538w, https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/08\/image-79-300x64.png 300w\" sizes=\"auto, (max-width: 538px) 100vw, 538px\" \/><\/figure>\n\n\n\n<h2 id=\"h-where-that-leaves-us\" class=\"wp-block-heading\">Where that leaves us<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An RFC specifies what the bits mean, and nothing in it governs how a storage engine chooses to order them.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">UUIDv7 delivers what it promises, a sortable timestamp at the start of the RFC byte sequence, and SQL Server applies the comparison order it has always applied, in which that timestamp carries almost no weight. Both behaviours are internally consistent. Put together, under the workload measured here, they produce no clustered-index locality whatsoever, and the version number in the identifier gives you no hint that the temporal ordering has been lost along the way.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So when the next identifier scheme arrives described as sequential, the version number and the printed form will settle very little. What counts is where the increasing portion ends up once the engine has applied its storage layout and its comparison order.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>UUIDv7 places its timestamp first, but SQL Server compares uniqueidentifier<br \/>\nvalues starting from the last bytes. Measured on 25,000 inserts: no gain over NEWID().<\/p>\n","protected":false},"author":157,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":"","_members_access_role":[],"_members_access_error":""},"categories":[368,99],"tags":[3030,4077,51],"type_dbi":[],"class_list":["post-46589","post","type-post","status-publish","format-standard","hentry","category-development-performance","category-sql-server","tag-development","tag-primary-key","tag-sql-server"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v28.4 (Yoast SEO v28.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Why UUIDv7 does not reduce fragmentation in SQL Server - dbi Blog<\/title>\n<meta name=\"description\" content=\"UUIDv7 places its timestamp first, but SQL Server compares uniqueidentifiervalues starting from the last bytes. Measured on 25,000 inserts: no gain over NEWID().\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Why UUIDv7 does not reduce fragmentation in SQL Server\" \/>\n<meta property=\"og:description\" content=\"UUIDv7 places its timestamp first, but SQL Server compares uniqueidentifiervalues starting from the last bytes. Measured on 25,000 inserts: no gain over NEWID().\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-04T11:30:42+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-04T11:30:44+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-5.png\" \/>\n\t<meta property=\"og:image:width\" content=\"265\" \/>\n\t<meta property=\"og:image:height\" content=\"43\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Louis Tochon\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Louis Tochon\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/\"},\"author\":{\"name\":\"Louis Tochon\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/e4195b0cb120295b3407a502c23e75b6\"},\"headline\":\"Why UUIDv7 does not reduce fragmentation in SQL Server\",\"datePublished\":\"2026-09-04T11:30:42+00:00\",\"dateModified\":\"2026-09-04T11:30:44+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/\"},\"wordCount\":1704,\"commentCount\":0,\"image\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2026\\\/09\\\/image-5.png\",\"keywords\":[\"development\",\"primary key\",\"SQL Server\"],\"articleSection\":[\"Development &amp; Performance\",\"SQL Server\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/\",\"name\":\"Why UUIDv7 does not reduce fragmentation in SQL Server - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2026\\\/09\\\/image-5.png\",\"datePublished\":\"2026-09-04T11:30:42+00:00\",\"dateModified\":\"2026-09-04T11:30:44+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/e4195b0cb120295b3407a502c23e75b6\"},\"description\":\"UUIDv7 places its timestamp first, but SQL Server compares uniqueidentifiervalues starting from the last bytes. Measured on 25,000 inserts: no gain over NEWID().\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2026\\\/09\\\/image-5.png\",\"contentUrl\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2026\\\/09\\\/image-5.png\",\"width\":265,\"height\":43},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Why UUIDv7 does not reduce fragmentation in SQL Server\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/e4195b0cb120295b3407a502c23e75b6\",\"name\":\"Louis Tochon\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g\",\"caption\":\"Louis Tochon\"},\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/author\\\/louistochon\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Why UUIDv7 does not reduce fragmentation in SQL Server - dbi Blog","description":"UUIDv7 places its timestamp first, but SQL Server compares uniqueidentifiervalues starting from the last bytes. Measured on 25,000 inserts: no gain over NEWID().","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/","og_locale":"en_US","og_type":"article","og_title":"Why UUIDv7 does not reduce fragmentation in SQL Server","og_description":"UUIDv7 places its timestamp first, but SQL Server compares uniqueidentifiervalues starting from the last bytes. Measured on 25,000 inserts: no gain over NEWID().","og_url":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/","og_site_name":"dbi Blog","article_published_time":"2026-09-04T11:30:42+00:00","article_modified_time":"2026-09-04T11:30:44+00:00","og_image":[{"width":265,"height":43,"url":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-5.png","type":"image\/png"}],"author":"Louis Tochon","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Louis Tochon","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/"},"author":{"name":"Louis Tochon","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/e4195b0cb120295b3407a502c23e75b6"},"headline":"Why UUIDv7 does not reduce fragmentation in SQL Server","datePublished":"2026-09-04T11:30:42+00:00","dateModified":"2026-09-04T11:30:44+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/"},"wordCount":1704,"commentCount":0,"image":{"@id":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/#primaryimage"},"thumbnailUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-5.png","keywords":["development","primary key","SQL Server"],"articleSection":["Development &amp; Performance","SQL Server"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/","url":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/","name":"Why UUIDv7 does not reduce fragmentation in SQL Server - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/#primaryimage"},"image":{"@id":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/#primaryimage"},"thumbnailUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-5.png","datePublished":"2026-09-04T11:30:42+00:00","dateModified":"2026-09-04T11:30:44+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/e4195b0cb120295b3407a502c23e75b6"},"description":"UUIDv7 places its timestamp first, but SQL Server compares uniqueidentifiervalues starting from the last bytes. Measured on 25,000 inserts: no gain over NEWID().","breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/#primaryimage","url":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-5.png","contentUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2026\/09\/image-5.png","width":265,"height":43},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/why-uuidv7-does-not-reduce-fragmentation-in-sql-server\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Why UUIDv7 does not reduce fragmentation in SQL Server"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/e4195b0cb120295b3407a502c23e75b6","name":"Louis Tochon","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/ce0ee48c64e763e6c4076e21c80729d15bc4493288aeb8695125c69082100e10?s=96&d=mm&r=g","caption":"Louis Tochon"},"url":"https:\/\/www.dbi-services.com\/blog\/author\/louistochon\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/46589","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/157"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=46589"}],"version-history":[{"count":50,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/46589\/revisions"}],"predecessor-version":[{"id":46729,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/46589\/revisions\/46729"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=46589"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=46589"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=46589"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=46589"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}