---
title: Distinct And Sketch Aggregates
url: https://nexyron.tech/docs/cypher/functions/aggregation/distinct-and-sketch-aggregates
description: Use this part of the aggregate surface when the main question is cardinality rather than total volume: exact unique counts, approximate unique counts, or grouped distinct aware sum
---

# Distinct And Sketch Aggregates

## Generic Description

Use this part of the aggregate surface when the main question is cardinality rather than total volume: exact unique counts, approximate unique counts, or grouped distinct-aware summaries.

Simple example:

```cypher
MATCH (u:User)-[:HAS_APP_SESSION]->(s:AppSession)
RETURN count(DISTINCT u.id) AS exact_users,
       approx_count_distinct(u.id) AS approx_users
```

## Consumer-Level Explanation

There are two distinct problems here:

-   exact uniqueness: “How many truly different values did I see?”
-   approximate uniqueness: “How many unique values did I likely see, but with lower memory cost?”

Nexyron supports both:

-   exact distinct counting with `count(DISTINCT expr)`
-   approximate distinct counting with `approx_count_distinct` and `hll_count`

This family matters because exact distinct counts can become expensive on large grouped workloads, while sketches can make dashboard-scale analytics cheaper without inventing a second query language.

## More Detailed Explanation

The approximate path uses a HyperLogLog-style sketch. That makes it useful when:

-   the grouped result set is large
-   exact duplicate tracking would be more expensive than the accuracy tradeoff is worth
-   you need mergeable cardinality state rather than raw unique values

The current implementation also supports state and merge forms for the approximate distinct family, which matters for:

-   partial aggregation
-   spill-backed grouped execution
-   aggregate-state materialization patterns

There is also one important semantic detail in the current implementation:

-   `approx_count_distinct(DISTINCT expr)` is redundant from a user-intent perspective
-   the engine now keeps that redundant shape on the same exact incremental aggregate-view subset instead of forcing a refresh boundary

That is the kind of small but important polish that keeps the Cypher surface predictable.

## Advanced Example

```cypher
MATCH (u:User)-[:HAS_APP_SESSION]->(s:AppSession)
TIME s.started_at BETWEEN datetime('2026-04-01T00:00:00Z') AND datetime('2026-04-30T23:59:59Z')
RETURN u.profile.location.country AS country,
       count(DISTINCT s.mode) AS distinct_modes,
       count(DISTINCT u.id) AS exact_users,
       approx_count_distinct(u.id) AS approx_users,
       approx_count_distinct(DISTINCT u.id) AS redundant_distinct_sketch
ORDER BY country
```

## Real Use Cases

-   daily active user dashboards where exactness is required for audits but approximate counts are enough for fast overview panels
-   grouped product analytics where unique users, sessions, or devices matter more than total events
-   distributed or partial aggregation workflows where mergeable cardinality state is more valuable than storing raw distinct sets

## Real Limitations And Tradeoffs

-   approximate distinct counts are not exact counts and should not be presented as audit-grade numbers
-   exact distinct counts can still be the right answer when correctness is non-negotiable, especially for billing, quotas, or compliance reporting
-   sketches help memory pressure, but they do not erase the cost of a poorly designed upstream match
