Skip to content

TSID

Time-Sorted Unique Identifier generator for DuckDB.

31,270
extension loads Β· last 90 days
On this page

Technical Overview

Sortable, time-ordered IDs minted in SQL

What this extension is for

  • β€’ Surrogate primary keys that sort by insert time: Use tsid() as a column DEFAULT. ORDER BY id returns insert order β€” no separate created_at index needed for chronological scans.
  • β€’ Audit-log and event identifiers: B-tree friendly: new rows append at the high end of the index instead of scattering randomly the way uuid() v4 does. Reduces page splits and improves cache locality on append-mostly tables.
  • β€’ Time-range pagination cursors: Because IDs are sortable strings, you can paginate by WHERE id < :cursor ORDER BY id DESC without joining a timestamp index β€” the Twitter snowflake trick, in pure SQL.
  • β€’ Embedded timestamp recovery: tsid_to_timestamp decodes the leading bits back to a TIMESTAMP. One column does the work of two.

How it works

  • β€’ Output type is VARCHAR, not BIGINT: tsid() returns a 32-character hex VARCHAR, the same width as a UUID hex string. Plain string comparison gives you time order β€” no special operator or cast required.
  • β€’ Independent calls per row: Each tsid() call is independent β€” every row in SELECT tsid() FROM range(N) produces a fresh TSID. Within the millisecond resolution of the timestamp prefix, exact insertion order across rows of a parallel-evaluated query isn't guaranteed; if a strict total order matters, sort by an explicit created_at column rather than by the ID.
  • β€’ Embedded timestamp: tsid_to_timestamp decodes the leading bits to a TIMESTAMP with sub-millisecond precision. Same primitive UUIDv7-aware tools use to extract creation time from a v7 UUID.
  • β€’ No external dependencies: The extension generates IDs in-process β€” no network call, no clock-server coordination. Safe to use as a column DEFAULT in CREATE TABLE without latency surprises.

TSID vs UUIDv4 vs UUIDv7 vs ULID

  • β€’ vs UUIDv4 (DuckDB built-in uuid()): UUIDv4 is random β€” great for collision avoidance, terrible for B-tree locality and unusable for time-ordered pagination. TSID wins anywhere insert order, recent-first scans, or index-append behavior matters.
  • β€’ vs UUIDv7 (RFC 9562): UUIDv7 is the IETF-standardized time-sortable UUID. Functionally TSID and UUIDv7 cover the same ground; UUIDv7 wins on portability (every modern UUID library understands it), TSID wins because DuckDB ships v4 only and TSID gives you the v7-shaped behavior without bringing your own generator.
  • β€’ vs ULID (ulid/spec): ULID is the same idea β€” millisecond-precision time prefix plus random tail β€” rendered in 26-character Crockford base32. TSID's hex rendering is two characters wider but lines up cleanly with how DuckDB users already format UUIDs, and avoids Crockford-base32 ambiguity edge cases.

Caveats

  • β€’ TSIDs leak creation time: The timestamp is recoverable by anyone holding the ID β€” that's the feature, but it's also a privacy and competitive-intelligence concern. Don't use TSIDs as public-facing identifiers for resources where the creation moment is sensitive (signup time, order placement, account creation). uuid() v4 stays the right choice for opaque external IDs.
  • β€’ Not an IETF or de-facto standard: Unlike UUIDv7 (RFC 9562) or ULID (widely adopted spec), the TSID rendering is library-specific. The conceptual primitive β€” time prefix + random tail β€” is portable; the exact 32-hex layout produced here may not parse with TSID libraries from other ecosystems such as f4b6a3/tsid-creator. Stay self-contained, or pin the format you serialize to disk.
  • β€’ VARCHAR storage, not BINARY: Each TSID occupies 32 bytes as text β€” twice the 16 bytes a binary UUID column needs in some engines. DuckDB's dictionary encoding compresses this well in practice, but plan for it on multi-billion-row tables where every byte counts.
  • β€’ Version pinning: The exposed surface is small (tsid, tsid_to_timestamp), but production schemas can depend on the persistent hex layout. Pin a known-good extension and DuckDB version when those identifiers are stored long-term.

Common Use Cases

Deep Dive

Technical Details

Install

INSTALL tsid FROM community;
LOAD tsid;

Quick Start

Single TSID

SELECT tsid();

Recover the embedded timestamp

SELECT tsid_to_timestamp(tsid()) AS created_at;

Bulk generation

SELECT tsid() AS id FROM range(1000);

Reference

Extension Contents

Quick reference to all available functions and settings organized by category.

Name Description
Generation
tsid() Generate a new Time-Sorted Unique Identifier β€” a 32-character hex string.
Inspection
tsid_to_timestamp() Extract the embedded timestamp from a TSID.

API Reference

Function Documentation

Practical Examples

Cookbook

Real-world recipes and patterns for common use cases.

Platform Support

Compatibility

Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.

Quick Facts

Release status Stable
Software License MIT
Pricing Free
Written In C++
Source Available Yes
View on GitHub
Usage
31,270
loads Β· last 90 days

Platforms

  • Linux x86_64 aarch64
  • Linux (musl) Not available
  • macOS Intel Apple Silicon
  • Windows x86_64
  • WASM eh mvp threads
Compiled binary sizes
Platform Architecture Size
Linux x86_64 3.20 MB
Linux aarch64 2.84 MB
macOS Intel 1.45 MB
macOS Apple Silicon 1.31 MB
Windows x86_64 7.38 MB
WASM eh 21.7 KB
WASM mvp 18.3 KB
WASM threads 21.9 KB

Compressed download size from the Haybarn extension repository.

DuckDB & Haybarn

Release calendar