uniqExact
Count distinct values exactly — the precise, memory-heavy counterpart to uniq().
Signature
uniqExact(column: T): UInt64Returns
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
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.
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.
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.