--- title: The pg_uuidv7 extension subtitle: Generate and manage time-ordered version 7 UUIDs in Postgres enableTableOfContents: true updatedOn: '2025-08-02T10:33:29.241Z' --- The `pg_uuidv7` extension allows you to generate and work with version 7 Universally Unique Identifiers (UUIDs) in Postgres. UUIDv7 is a newer UUID format designed to be time-ordered and sortable, which offers significant benefits for database performance, especially when used as primary keys or in time-series data. Unlike traditional random UUIDs (like Version 4), UUIDv7 embeds a Unix timestamp in its leading bits, followed by random bits. This structure ensures that newly generated UUIDs are roughly sequential, which is highly beneficial for database indexing (e.g., B-trees) and can improve data locality, leading to faster queries and insertions. ## Enable the `pg_uuidv7` extension You can enable the extension by running the following `CREATE EXTENSION` statement in the [Neon SQL Editor](/docs/get-started/query-with-neon-sql-editor) or from a client such as [psql](/docs/connect/query-with-psql-editor) that is connected to your Neon database. ```sql CREATE EXTENSION IF NOT EXISTS pg_uuidv7; ``` **Version availability:** Please refer to the [list of all extensions](/docs/extensions/pg-extensions) available in Neon for up-to-date extension version information. ## Core functions The `pg_uuidv7` extension provides a concise set of functions for generating and manipulating version 7 UUIDs. ### `uuid_generate_v7()` This is the primary function for generating new version 7 UUIDs. It creates a UUID incorporating the current Unix timestamp (with millisecond precision) in its most significant bits, followed by randomly generated bits for the remainder. ```sql SELECT uuid_generate_v7(); -- 0196ce37-0758-736d-a33b-ad3f017359e3 (example output) ``` ### `uuid_v7_to_timestamptz(uuid_v7 UUID)` This function extracts the embedded timestamp from a version 7 UUID and returns it as a `TIMESTAMPTZ` (timestamp with time zone) value. ```sql SELECT uuid_v7_to_timestamptz('0196ce37-0758-736d-a33b-ad3f017359e3'); -- 2025-05-14 09:53:55.032+00 ``` ### `uuid_timestamptz_to_v7(ts TIMESTAMPTZ, zero_random_bits BOOLEAN DEFAULT false)` This function converts a given `TIMESTAMPTZ` value into a version 7 UUID. It takes two arguments: 1. `ts TIMESTAMPTZ`: The timestamp to embed in the UUID. 2. `zero_random_bits BOOLEAN` (optional, defaults to `false`): - If `false` (default), the random bits portion of the UUID will be filled with new random data. This is useful for creating a UUID tied to a specific past or future time but still unique. - If `true`, the random bits portion of the UUID will be set to all zeros. This is particularly useful for creating boundary UUIDs for time-range queries (e.g., the earliest possible UUID for a given timestamp). #### Generating a UUID for a specific timestamp with random bits ```sql SELECT uuid_timestamptz_to_v7('2025-05-14 10:53:55.032+00'); ``` Example output (random part will vary): ```text uuid_timestamptz_to_v7 -------------------------------------- 0196ce6d-f5d8-7a89-8e7a-06b371fc5d70 (1 row) ``` #### Generating a boundary UUID for a specific timestamp (random bits zeroed) ```sql SELECT uuid_timestamptz_to_v7('2025-05-14 10:53:55.032+00', true); ``` Example output (random part will be fixed): ```text uuid_timestamptz_to_v7 -------------------------------------- 0196ce6d-f5d8-7000-8000-000000000000 (1 row) ``` ## Key advantages of UUIDv7 Using version 7 UUIDs in your database schema can provide several advantages over traditional UUIDs, especially in scenarios where time-based ordering is important. Here are some key benefits: 1. **Improved Indexing performance:** Because UUIDv7s are time-ordered, new entries are typically inserted towards the end of an index (e.g., a B-tree index on a UUIDv7 primary key). This leads to better data locality, reduced page splits, and less index fragmentation compared to random UUIDs (like v4). This can significantly boost insert performance and make range scans more efficient. 2. **Natural sortability:** UUIDv7s can be sorted chronologically by their value, which is useful for ordering records by creation time without needing a separate timestamp column for this purpose. 3. **Distributed systems friendliness:** Like all UUIDs, v7 can be generated independently across multiple nodes without coordination, ensuring global uniqueness. The time-ordered property adds benefits for distributed databases that might later need to merge or sort data by generation time. ## Example usage Let's explore some common use cases for `pg_uuidv7`. ### Using UUIDv7 as a primary key UUIDv7 is an excellent candidate for primary keys, especially for tables where data is often queried or inserted based on time. ```sql CREATE TABLE events ( event_id UUID PRIMARY KEY DEFAULT uuid_generate_v7(), event_type TEXT NOT NULL, event_data JSONB ); INSERT INTO events (event_type, event_data) VALUES ('user_login', '{"user_id": 101, "ip": "192.168.1.10"}'), ('page_view', '{"user_id": 101, "url": "/products/awesome-widget"}'), ('purchase', '{"user_id": 205, "item_id": "XYZ123", "amount": 99.99}') RETURNING event_id, event_type; ``` Example output: ```text event_id | event_type --------------------------------------+------------ 0196e801-a0d6-7af7-a308-13057189ef3f | user_login 0196e801-a0ec-7cf7-a4f2-2a4a73c58688 | page_view 0196e801-a0ed-7fdb-9858-dcd601cadc26 | purchase (3 rows) ``` Notice how the `event_id` values are largely sequential, reflecting their insertion order. ### Time-range queries The ability to convert timestamps to UUIDv7s (especially with zeroed random bits) is very useful for performing efficient time-range queries directly on the UUIDv7 primary key. Suppose we want to find all events that occurred between May 14, 2025, and May 24, 2025. We can use the `uuid_timestamptz_to_v7()` function to create boundary UUIDs for our query: ```sql SELECT * FROM events WHERE event_id >= uuid_timestamptz_to_v7('2025-05-14', true) -- Start of May 14th AND event_id < uuid_timestamptz_to_v7('2025-05-24', true); -- Start of May 24th ``` This query can efficiently use an index on `event_id` to find matching records. ## Comparison with UUID4 UUIDv4 is purely random. While excellent for uniqueness, its randomness leads to poor index locality and fragmentation when used as a primary key for time-sensitive data. UUIDv7 directly addresses this by being time-ordered. ## Conclusion The `pg_uuidv7` extension provides a robust and efficient way to work with version 7 UUIDs in Postgres. By embedding a timestamp, UUIDv7s offer the global uniqueness of traditional UUIDs while also being chronologically sortable. This makes them an excellent choice for primary keys and indexed columns in applications where time-ordering and query performance on time-based data are critical. ## Resources - [fboulnois/pg_uuidv7 GitHub repository](https://github.com/fboulnois/pg_uuidv7) - [UUID version 7](https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-7). - [uuid-ossp](/docs/extensions/uuid-ossp)