ClickHouse Functions/Aggregate Functions

uniqExact

Count distinct values exactly — the precise, memory-heavy counterpart to uniq().

Signature

uniqExact(column: T): UInt64

Returns

UInt64

What it does

Returns the exact number of distinct values in a column. Unlike uniq(), it holds every distinct value in memory, so it is accurate but costly on high-cardinality columns.

uniqExact is the exact-cardinality aggregate in ClickHouse: it computes COUNT(DISTINCT column) with no approximation. To guarantee accuracy it keeps a hash set of every distinct value it sees, which makes it memory-heavy and slower than uniq() on high-cardinality columns like user_id or session_id. Reach for uniqExact only when exactness is a hard requirement — billing, compliance, or invoice reconciliation — and use uniq() for dashboards and exploratory analytics where a small error is acceptable. A useful detail: COUNT(DISTINCT x) maps to uniqExact by default (controlled by the count_distinct_implementation setting), so the two are equivalent unless you change that setting.

Notes

  • uniqExact stores every distinct value in memory, so it can be slow and RAM-hungry on high-cardinality columns — prefer uniq() unless exactness is required.
  • COUNT(DISTINCT x) compiles to uniqExact by default via the count_distinct_implementation setting, so the two produce identical results.
  • UInt64 counts come back from ClickHouse as strings in JavaScript — parse before doing arithmetic on the result.
  • hypequery's .countDistinct('col', 'alias') emits COUNT(DISTINCT col), which is the exact (uniqExact) path; use uniq() via selectExpr for the fast approximate count.

Example SQL

uniqExact in ClickHouse SQL

uniqExact-example.sql

TypeScript with hypequery

Use uniqExact in a typed TypeScript query

hypequery gives you a type-safe query builder for ClickHouse. The generated schema maps your ClickHouse columns to TypeScript types, and raw SQL expressions let you incorporate functions like uniqExact when you need them inside a builder query.

exact-distinct-users.ts

Common questions

What developers search for with uniqExact

COUNT(DISTINCT) in ClickHouse

COUNT(DISTINCT x) maps to uniqExact by default via count_distinct_implementation. In hypequery, .countDistinct('user_id', 'exact_users') emits exactly that — an exact distinct count.

uniqExact vs uniq

uniqExact is exact but keeps every value in memory; uniq() is approximate, fast, and fixed-memory. Use uniqExact for billing and compliance, uniq() for dashboards.

Exact distinct count in TypeScript

hypequery gives you both paths with typed results: native .countDistinct for the exact count, or selectExpr('uniqExact(user_id)', 'exact_users') to name the function directly.

FAQ

Frequently asked questions about uniqExact

What is the difference between uniqExact and uniq in ClickHouse?

uniqExact returns the exact distinct count by keeping every value in memory, while uniq() returns an approximate count using an HLL-like algorithm with small, fixed memory. Use uniqExact when exactness is required and uniq() when speed matters more than a tiny error.

Does COUNT(DISTINCT x) use uniqExact?

Yes. By default ClickHouse rewrites COUNT(DISTINCT x) to uniqExact(x), controlled by the count_distinct_implementation setting. In hypequery, .countDistinct('col', 'alias') emits COUNT(DISTINCT col), so it takes the exact path.

When should I avoid uniqExact?

Avoid it on high-cardinality columns in latency-sensitive queries — it stores all distinct values and can consume significant memory. Prefer uniq() for dashboards and exploratory analytics unless the count must be exact.

Related functions

Functions used alongside uniqExact

Next step

Use uniqExact in a type-safe TypeScript query

hypequery generates TypeScript types from your ClickHouse schema. Use uniqExact alongside the builder, and reach for raw SQL expressions when the function is not exposed as a dedicated helper.