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

Random UUID v4 keys can scatter B-tree inserts. UUID v7 can improve locality in PostgreSQL and with unswapped binary bytes in MySQL, but SQL Server uniqueidentifier uses its own comparison order. Do not assume a UUID v7 clustered key is sequential there; benchmark your workload and consider a separate sequential clustering key. Creation time in v7 may also be visible to public clients.

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.

Primary references

Standards and technical sources

The implementation and guidance on this page are checked against these standards and platform documentation sources.

How to Store UUIDs in PostgreSQL, MySQL & SQL Server