Databases

How to Store UUIDs in PostgreSQL, MySQL and SQL Server

A practical, database-by-database guide to storing UUIDs efficiently: native types, indexing, and the UUID v4 vs v7 decision for each engine.

Native uuid in Postgres, BINARY(16) in MySQL, uniqueidentifier in SQL Server: how to store UUIDs efficiently in each engine, and why insert order matters.

PostgreSQL: use the native uuid type

PostgreSQL has a first-class uuid type. It validates values on the way in and stores them in 16 bytes, which is far more efficient than a text column. Use it for any UUID field. If you also want the database to generate values, a UUID v7 function keeps inserts ordered; otherwise gen_random_uuid() produces UUID v4.

CREATE TABLE orders (
  id uuid PRIMARY KEY,
  customer_id uuid NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX ON orders (customer_id);

MySQL: BINARY(16) or CHAR(36)?

MySQL has no dedicated UUID type, so you choose the representation. CHAR(36) is human-readable and easy to inspect but uses more than twice the space and indexes less tightly. BINARY(16) stores the raw bytes compactly and is the better default for large tables, at the cost of converting with UUID_TO_BIN and BIN_TO_UUID.

If you use BINARY(16) with time-ordered UUIDs, pass the swap flag to UUID_TO_BIN so the timestamp bytes lead, preserving index locality.

-- Compact, index-friendly storage
INSERT INTO users (id) VALUES (UUID_TO_BIN(?, 1));
SELECT BIN_TO_UUID(id, 1) AS id FROM users;

SQL Server: uniqueidentifier and clustering

SQL Server stores UUIDs as uniqueidentifier (16 bytes) and developers usually call them GUIDs. The classic pitfall is making a random GUID the clustered primary key, which fragments the clustered index on every insert. Options are to use a nonclustered primary key, cluster on a sequential column, or use a sequential-GUID strategy so new keys sort in order.

The version decision applies everywhere

Across all three engines the same principle holds: random UUID v4 keys scatter inserts, and time-ordered UUID v7 keys append. On small or low-write tables either is fine. On high-write tables, prefer an ordered scheme and store the bytes compactly. Keep public exposure separate from storage if the creation time embedded in v7 is sensitive.

EngineRecommended typeNote
PostgreSQLuuidNative, compact, validated
MySQLBINARY(16)Use swap flag for ordered UUIDs
SQL ServeruniqueidentifierAvoid random clustered PK

FAQ

How to Store UUIDs in PostgreSQL, MySQL and SQL Server questions

Is CHAR(36) or BINARY(16) better for MySQL UUIDs?

BINARY(16) is more space- and index-efficient for large tables; CHAR(36) is easier to read and debug. For high-volume tables, prefer BINARY(16) with conversion functions.

Should UUIDs be the clustered primary key in SQL Server?

A random GUID clustered key fragments the index. Use a nonclustered primary key, a sequential GUID, or cluster on another ordered column.

How to Store UUIDs in PostgreSQL, MySQL & SQL Server