Use UUID v7 in PostgreSQL
PostgreSQL 18 adds built-in uuidv7(). Use it as a column default to create IDs during insertion, or store IDs generated by your application in a uuid column. Check your server with SHOW server_version; before choosing an approach.
PostgreSQL 18+: generate a v7 primary key
Run this example in one SQL session. The temporary table disappears when the session ends; use CREATE TABLE in your schema migration for a persistent table.
-- PostgreSQL 18+. TEMP keeps this demo within the current session.
CREATE TEMP TABLE events_demo (
id uuid PRIMARY KEY DEFAULT uuidv7(),
description text NOT NULL,
created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO events_demo (description)
VALUES ('Example event')
RETURNING id, uuid_extract_version(id) AS version,
uuid_extract_timestamp(id) AS embedded_time;The returned version is 7. Omit id or supply DEFAULT to use the default; passing NULL explicitly fails the primary-key constraint. Keep created_at for your chosen timestamp semantics: here CURRENT_TIMESTAMP records the transaction start time.
References: PostgreSQL 18 UUID functions, column defaults, and current date/time functions.
Read the version and embedded timestamp
SET TIME ZONE 'UTC';
SELECT uuid_extract_version(id) AS version,
uuid_extract_timestamp(id) AS embedded_time
FROM (VALUES ('017f22e2-79b0-7cc3-98c4-dc0c0c07398f'::uuid)) AS example(id);
-- version: 7, embedded_time: 2022-02-22 19:22:22+00The extracted value is a timestamp with time zone; the session time zone controls its display. For the same example in your browser, use the UUID v7 timestamp converter. Use the UUID decoder to see which bits hold the version, variant, and time.
An embedded timestamp is not proof of insertion or commit time. Applications can supply their own timestamp, and PostgreSQL’s generator supports an optional time shift. Keep explicit event timestamps when that distinction matters.
PostgreSQL 17 and earlier: generate in the application
These servers do not have the built-in uuidv7() function. The UUID data type can still store v7 values. Generate in your application, bind the ID as a query parameter, and insert into a uuid column. This literal example demonstrates storage:
-- Supply an ID generated by your application.
CREATE TEMP TABLE app_events_demo (id uuid PRIMARY KEY);
INSERT INTO app_events_demo (id)
VALUES ('017f22e2-79b0-7cc3-98c4-dc0c0c07398f'::uuid)
RETURNING id;Use Guid.CreateVersion7() on .NET 9+, or a UUID library with v7 support in your application. PostgreSQL 17’s gen_random_uuid() generates v4, and its timestamp extractor handles v1, not v7; see the PostgreSQL 17 functions. Use the browser converter above when you need to inspect a v7 timestamp on an older server.
Existing tables and performance expectations
Changing a default affects future inserts that use it; existing IDs and foreign keys stay as they are. A UUID column can hold both v4 and v7. Test migrations with your application’s ID generation and constraints before changing production data.
Time-prefixed IDs may improve insertion locality, but throughput and index size depend on the workload, concurrency, storage, and arrival order. No benchmark gain is claimed here. Explicitly ORDER BY the field matching your intended ordering; an ID does not establish transaction order.
Need sample rows first? Generate a batch with SQL VALUES export, or generate one UUID v7 for inspection.