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.

For implementation context, continue with SQL, Database Guide, and UUID v7. These pages cover the closest generator, comparison, validation or storage decisions without repeating this guide.

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. PostgreSQL 18 includes uuidv7() for UUID v7 and uuidv4() for UUID v4; gen_random_uuid() remains an alias for UUID v4. On older releases, generate v7 in your application or with a carefully reviewed extension.

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.

UUID v7 already puts its timestamp first, so store it with UUID_TO_BIN(?) without MySQL’s swap flag. The swap flag was designed to rearrange UUID v1 fields and should only be used when the corresponding read and write paths deliberately use that representation.

-- Compact storage for a canonical UUID v7 string
INSERT INTO users (id) VALUES (UUID_TO_BIN(?));
SELECT BIN_TO_UUID(id) 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)Do not byte-swap UUID v7
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