The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Inventory available collationsSELECT collname, collprovider, collisdeterministic, collversion FROM pg_collation ORDER BY collname;View examples
Decode provider metadataSELECT collname, CASE collprovider WHEN 'b' THEN 'builtin' WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END FROM pg_collation;View examples
Create a deterministic ICU collationCREATE COLLATION app.en_sort (provider = icu, locale = 'en-US');View examples
Create a builtin collation aliasCREATE COLLATION app.unicode_fast (provider = builtin, locale = 'PG_UNICODE_FAST');View examples
Copy a known collationCREATE COLLATION app.bytewise FROM pg_catalog."C";View examples
Create case-insensitive comparisonCREATE COLLATION app.case_insensitive (provider = icu, locale = 'und-u-ks-level2', deterministic = false);View examples
Ignore accent differencesCREATE COLLATION app.base_letter (provider = icu, locale = 'und-u-ks-level1-kc-true', deterministic = false);View examples
Set a column collationdisplay_name text COLLATE app.en_sort NOT NULLView examples
Override one expressionSELECT display_name FROM app.people ORDER BY display_name COLLATE app.en_sort;View examples
Resolve mixed-collation inputSELECT left_text COLLATE app.en_sort < right_text COLLATE app.en_sort FROM app.comparisons;View examples
Enforce insensitive uniquenessCREATE UNIQUE INDEX users_name_ci_key ON app.users (name COLLATE app.case_insensitive);View examples
Index a specific sort orderCREATE INDEX people_name_en_idx ON app.people (display_name COLLATE app.en_sort);View examples
Detect collation version driftSELECT collname FROM pg_collation WHERE collversion IS DISTINCT FROM pg_collation_actual_version(oid);View examples
Rebuild collation-dependent indexesREINDEX INDEX CONCURRENTLY app.people_name_en_idx;View examples
Record the current provider versionALTER COLLATION app.en_sort REFRESH VERSION;View examples
Delegate collation creationGRANT USAGE, CREATE ON SCHEMA app TO locale_admin;View examples

A PostgreSQL collation determines how collatable text is ordered and, for nondeterministic ICU collations, which byte-distinct strings compare as equal. Treat that behavior as part of the data model: choose a provider deliberately, bind important expressions and indexes to schema-qualified collations, and rehearse provider upgrades because changed sort rules can invalidate stored index ordering.

Step by step

Detailed examples

01

Choose a provider whose stability matches the contract

PostgreSQL 18 exposes builtin, ICU, and libc providers. ICU offers BCP 47 language tags and rich Unicode tailoring but requires an ICU-enabled build; libc names and behavior are operating-system dependent. The builtin provider accepts C, C.UTF-8, or PG_UNICODE_FAST, with PG_UNICODE_FAST new in PostgreSQL 18, available only for UTF8 databases, and stable within a PostgreSQL major version. Inspect the actual server rather than assuming a locale name exists on every deployment.

Audit usable collation definitions
SELECT n.nspname AS schema_name, c.collname,
       CASE c.collprovider
         WHEN 'b' THEN 'builtin'
         WHEN 'c' THEN 'libc'
         WHEN 'i' THEN 'icu'
         WHEN 'd' THEN 'database default'
       END AS provider,
       c.collisdeterministic, c.collencoding, c.collversion
FROM pg_collation AS c
JOIN pg_namespace AS n ON n.oid = c.collnamespace
ORDER BY n.nspname, c.collname;
Back to quick reference ↑
02

Create stable, schema-qualified application names

A collation is a schema object and CREATE COLLATION requires CREATE on the destination schema. Put user-defined collations in an application-owned schema so pg_dump preserves them, and schema-qualify use sites to avoid search_path ambiguity. IF NOT EXISTS proves only that a name exists, not that its provider, locale, rules, or determinism match. CREATE COLLATION is transactional, but it takes a self-conflicting SHARE ROW EXCLUSIVE lock on pg_collation, so serialize migration batches that create collations.

Define explicit ICU and PostgreSQL 18 builtin contracts
BEGIN;
CREATE SCHEMA IF NOT EXISTS app AUTHORIZATION app_owner;
CREATE COLLATION app.en_sort (
  provider = icu,
  locale = 'en-US'
);
CREATE COLLATION app.unicode_fast (
  provider = builtin,
  locale = 'PG_UNICODE_FAST'
);
COMMIT;
Back to quick reference ↑
03

Use nondeterministic ICU collations only for intentional equivalence

Setting deterministic = false stops PostgreSQL from breaking provider-equal strings with a bytewise tie-breaker. With a suitable ICU tag this supports case-insensitive, accent-insensitive, or normalization-aware equality without lowercasing stored data. It is supported only by ICU. The performance tradeoffs are real: comparisons cost more, B-tree deduplication is disabled, and some pattern-matching operations or operator/index combinations may be unavailable. Test every operator used by the workload on the target PostgreSQL major and ICU version.

Define and verify an insensitive equality contract
CREATE COLLATION app.case_insensitive (
  provider = icu,
  locale = 'und-u-ks-level2',
  deterministic = false
);

SELECT 'Résumé' COLLATE app.case_insensitive = 'RÉSUMÉ' AS same_casefold,
       U&'\0061\0308' COLLATE app.case_insensitive = U&'\00E4' AS same_normalized;
Back to quick reference ↑
04

Bind comparison semantics at the narrowest useful scope

A column collation becomes the implicit collation of expressions derived from that column. An explicit COLLATE clause overrides implicit rules and is often safer for one report or cross-locale sort. Combining expressions with incompatible implicit collations can leave the result indeterminate and cause an error when an ordering or comparison operator needs a collation. Apply one explicit, schema-qualified collation to resolve that conflict rather than relying on session search_path.

Store one default while supporting another report order
CREATE TABLE app.people (
  person_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  display_name text COLLATE app.en_sort NOT NULL
);

SELECT person_id, display_name
FROM app.people
ORDER BY display_name COLLATE pg_catalog."C", person_id;
Back to quick reference ↑
05

Make index collation match the query and uniqueness rule

A B-tree index has a collation as part of its definition. The planner can use it for compatible comparisons and ordering, but a query using another collation generally needs another index or a sort. A unique index under a nondeterministic collation enforces the provider's equality relation, so visually or bytewise distinct values may conflict. Foreign-key text columns in PostgreSQL 18 must use deterministic collations or the same nondeterministic collation; confirm cross-version behavior before mixed-major migrations.

Separate display ordering from insensitive identity
CREATE INDEX people_name_en_idx
  ON app.people (display_name COLLATE app.en_sort);

CREATE UNIQUE INDEX people_name_ci_key
  ON app.people (display_name COLLATE app.case_insensitive);

EXPLAIN (COSTS OFF)
SELECT display_name
FROM app.people
ORDER BY display_name COLLATE app.en_sort;
Back to quick reference ↑
06

Rebuild dependents before refreshing a changed version

ICU, libc, and some database-default collations record provider versions. An operating-system, ICU, or major-version upgrade can change sort rules while an index still reflects the old order, risking wrong results or apparent corruption. Detect mismatches, identify dependencies, rebuild each affected index or materialized ordering, validate the application, and only then run REFRESH VERSION. REFRESH VERSION changes catalog metadata; it does not inspect or rebuild dependents. REINDEX CONCURRENTLY reduces write blocking but takes longer, uses extra space, and has transaction-block restrictions.

Find drift and its dependent objects
SELECT n.nspname, c.collname, c.collversion AS recorded_version,
       pg_collation_actual_version(c.oid) AS actual_version
FROM pg_collation AS c
JOIN pg_namespace AS n ON n.oid = c.collnamespace
WHERE c.collversion IS DISTINCT FROM pg_collation_actual_version(c.oid);

SELECT pg_describe_object(d.refclassid, d.refobjid, d.refobjsubid) AS collation,
       pg_describe_object(d.classid, d.objid, d.objsubid) AS dependent_object
FROM pg_depend AS d
JOIN pg_collation AS c ON d.refclassid = 'pg_collation'::regclass AND d.refobjid = c.oid
WHERE c.collversion IS DISTINCT FROM pg_collation_actual_version(c.oid)
ORDER BY 1, 2;
Use an ordered maintenance runbook
-- Run outside a transaction block when using CONCURRENTLY.
REINDEX INDEX CONCURRENTLY app.people_name_en_idx;

-- Refresh only after every dependent object has been rebuilt and checked.
ALTER COLLATION app.en_sort REFRESH VERSION;
Back to quick reference ↑
07

Treat locale DDL as privileged, versioned infrastructure

Do not let untrusted roles create collations in schemas used by application search paths. Own application collations with a migration role, grant only the required schema privileges, and qualify both the schema and collation name in DDL. Collation objects replicate through logical DDL only if your deployment system applies that DDL; PostgreSQL logical replication does not copy schema changes. Physical standbys inherit catalog and WAL changes, but every server must provide compatible locale libraries. Record server major, provider, locale, rules, and actual version in deployment evidence.

Limit locale administration and capture deployment evidence
REVOKE CREATE ON SCHEMA app FROM PUBLIC;
GRANT USAGE, CREATE ON SCHEMA app TO locale_admin;

SELECT current_setting('server_version') AS server_version,
       n.nspname, c.collname, c.collprovider, c.collisdeterministic,
       c.collversion, pg_collation_actual_version(c.oid) AS actual_version
FROM pg_collation AS c
JOIN pg_namespace AS n ON n.oid = c.collnamespace
WHERE n.nspname = 'app'
ORDER BY c.collname;
Back to quick reference ↑

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. PostgreSQL Global Development GroupPostgreSQL 18: Collation Supportpostgresql.org
  2. PostgreSQL Global Development GroupPostgreSQL 18: CREATE COLLATIONpostgresql.org
  3. PostgreSQL Global Development GroupPostgreSQL 18: ALTER COLLATIONpostgresql.org
  4. PostgreSQL Global Development GroupPostgreSQL 18: pg_collationpostgresql.org
  5. PostgreSQL Global Development GroupPostgreSQL 18: REINDEXpostgresql.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback