← Previous: Module 1 - Fundamentals 🏠 Home Next: Module 3 - Sharding β†’

πŸ“Š MODULE 2

Table Engines & Data Modeling
Duration: 4-6 hours | Week 1-2
πŸ“– What is It?

Table Engines & Data Modeling in ClickHouse

Table Engines are the core architecture components that determine how ClickHouse stores, processes, and retrieves data. Choosing the right engine is critical for performance and query optimization.

Data Modeling encompasses primary keys, sorting keys, partitioning strategies, data types, and codecs - all working together to create an optimal schema for your analytical workloads.

Why Table Engines Matter

⚑

Performance

Right engine + schema = 10-100x faster queries

πŸ’Ύ

Storage Efficiency

Compression, codecs, and data types optimize disk usage

🎧

Specialized Use Cases

Different engines for updates, aggregations, and deduplication

🎯

Query Optimization

Primary keys, sorting, and partitioning guide query execution

The MergeTree Family

Engine Purpose Best For
MergeTree Base engine for most use cases General-purpose analytics, time-series, events
ReplacingMergeTree Handles updates by replacing old versions User profiles, state snapshots, dimension tables
SummingMergeTree Pre-aggregates numeric columns during merges Counters, metrics, pre-aggregated data
AggregatingMergeTree Stores aggregation function states Complex aggregations, materialized views
CollapsingMergeTree Cancels out old rows with new ones Status changes, events with cancellation
VersionedCollapsingMergeTree Enhanced collapsing with version support Complex state management, version tracking

MergeTree Engine Internals

Partition: 202601
Part 1
Rows: 10,000
Primary Index: Sparse
Data Blocks: Compressed
Part 2
Rows: 15,000
Primary Index: Sparse
Data Blocks: Compressed
Partition: 202602
Part 1
Rows: 8,000
Primary Index: Sparse
Data Blocks: Compressed
Part 2
Rows: 12,000
Primary Index: Sparse
Data Blocks: Compressed
Inside Each Part:
Primary Index
Sparse index for quick lookups
Column Files
Separate .bin file per column
Data Blocks
Compressed with codecs

Key Concepts

Primary Key vs Sorting Key

Primary Key: Used for filtering (WHERE clauses). Defines which columns are used for pruning partitions and blocks.

Sorting Key: Determines physical order of data. Can be different from primary key.

Rule of Thumb: Primary key is usually the first 1-3 columns of sorting key.

Partitioning Strategy

Partition: Logical grouping of data (by date, month, region, etc.)

Benefits: Faster deletes, parallel processing, manageable data movement

Common: Partition by date/month for time-series data

Data Types Impact

Choosing right types affects compression and speed:

  • UInt8/16/32/64: Best compression for integers
  • Decimal: For financial data (fixed precision)
  • LowCardinality: 10-100x compression for enums
  • String: Avoid if possible, use enums instead

Codecs for Compression

Specialized compression algorithms:

  • DoubleDelta: Timestamps, monotonic sequences
  • Gorilla: Floating-point numbers, metrics
  • Delta: Integer deltas (increments)
  • ZSTD: General-purpose, configurable
πŸš€ Quick Start

1. Create Your First Optimized Table

2. Understanding ReplacingMergeTree

3. Understanding SummingMergeTree

πŸ’» Commands Reference

Engine-Specific Commands

MergeTree (Basic)

ReplacingMergeTree (Updates)

SummingMergeTree (Aggregations)

Partitioning and TTL Commands

Primary Key and Sorting Key Commands

Data Types and Codecs

✨ Best Practices

1. Choosing the Right Engine

Use MergeTree When:

  • Append-only workloads
  • Time-series or event data
  • No updates needed
  • Standard analytics queries

Use ReplacingMergeTree When:

  • Dimension tables with updates
  • User profiles or settings
  • State snapshots
  • Deduplication needed

Use SummingMergeTree When:

  • Pre-aggregated data
  • Counters and metrics
  • Daily/hourly rollups
  • Numeric columns to sum

Use AggregatingMergeTree When:

  • Complex aggregation states
  • Materialized views
  • Advanced analytics
  • State-based aggregations

2. Optimal Partitioning Strategy

❌ Too Many Partitions

Problem: 365+ partitions/year, metadata overhead

βœ… Optimal Partitions

Benefit: Only 12 partitions/year, easy data cleanup

3. Primary Key Design

❌ High Cardinality First

Problem: Poor compression, inefficient pruning

βœ… Low Cardinality First

Benefit: 10-100x better compression, faster queries

4. Data Type Selection

Use Right Data Types for Compression

  • UInt8/16/32/64: Use smallest type that fits your range
  • LowCardinality(String): 10-100x compression for enums (< 10k values)
  • Decimal(P, S): Financial data - use instead of Float
  • Date/DateTime: Always use date types, not strings
  • Enum: Better than String for fixed values
  • Avoid String: String columns are not compressed well

5. TTL Policies for Data Lifecycle

TTL for Rows (Auto-delete)

Automatically deletes rows after 90 days

TTL for Column (Move to S3)

Moves old data to cheaper storage

🎯 When to Use Each Engine

Decision Matrix

Scenario Recommended Engine Example Use Case
Append-only events, metrics, logs MergeTree Website events, server metrics, application logs
Dimension tables, user profiles, config ReplacingMergeTree User settings, product catalog, pricing
Pre-aggregated metrics, counters, rollups SummingMergeTree Daily stats, hourly rollups, aggregated counters
Complex aggregation state, cardinality tracking AggregatingMergeTree HyperLogLog for unique counts, complex statistics
State transitions, event cancellation CollapsingMergeTree Order status changes, billing events

Real-World Example: E-commerce Analytics

  • Raw Events Table: MergeTree - store all clicks, views, purchases
  • Product Dimensions: ReplacingMergeTree - product info changes over time
  • Daily Rollups: SummingMergeTree - pre-aggregate daily stats by category
  • Session Tracking: CollapsingMergeTree - track open/close sessions
πŸ† Real-World Results
50-100x

Compression Ratio

Optimal schema design with codecs and data types

10-1000x

Query Speedup

Right primary key = massive performance improvement

70%

Cost Reduction

Less storage needed due to compression

99.99%

Query Success

Proper schema design reduces timeouts

Case Study: SaaS Analytics Platform

Challenge: 500 million events/day, slow queries on dashboard

Before Optimization:

  • Storage: 100GB/day raw data
  • Query time: 5-10 seconds for dashboards
  • Using generic MergeTree, all columns with String type

Schema Changes:

  • Used LowCardinality for country, event_type, user_type
  • Added DoubleDelta codec for timestamps
  • Optimized primary key: (event_type, event_date, user_id)
  • Monthly partitioning instead of daily

Results:

  • Storage: 100GB β†’ 2GB (50x compression!)
  • Query time: 5-10s β†’ 200-500ms (20-50x faster)
  • Infrastructure cost: $50k/month β†’ $5k/month (10x reduction)
πŸ“‹ Ready-to-Use Templates

Template 1: Optimized Events Table

Template 2: Dimension Table with Updates

Template 3: Pre-Aggregated Metrics

Template 4: Logs Table with Optimal Compression

πŸ”₯ Advanced Patterns

1. Materialized View with Aggregation

2. Multi-Level Partitioning

3. Schema Evolution with ALTER

4. Using Sampling for Approximate Results

5. Primary Key vs Sorting Key Deep Dive

πŸ“š Resources & References

πŸ“˜ View in Notion

Access this module in your Notion workspace for note-taking and tracking your progress.

Open in Notion β†’

Key Takeaways from Module 2

  • βœ… Choose MergeTree for most append-only workloads
  • βœ… Use ReplacingMergeTree for dimension tables with updates
  • βœ… Use SummingMergeTree for pre-aggregated metrics
  • βœ… Partition by time (month/day) to enable efficient data management
  • βœ… Order by low-cardinality columns first for better compression
  • βœ… Use LowCardinality for enums (< 10k unique values)
  • βœ… Apply codecs (DoubleDelta, Gorilla, Delta) for specialized data
  • βœ… Implement TTL policies for automatic data cleanup
  • βœ… Primary key β‰  Sorting key (primary key is for pruning)
  • βœ… Test schema changes in development first

Ready for Module 3? Next, we'll cover Sharding & Distributed Tables!

🐳 Hands-on Docker Demo for this Module

A self-contained ClickHouse stack you can run locally. Every step is reproducible; the README below walks through each concept with diagrams and measured outcomes.

cd code-examples/demos/module-2-table-engines
./up.sh        # bring stack up, wait for health
./run.sh       # run the demo (idempotent β€” re-runnable)
./down.sh      # tear down + drop volumes

πŸ“Ί Live demo capture

Recorded with vhs: the actual terminal output from ./run.sh running on this very stack.

Module demo GIF
Animated GIF, ~479 KB. Right-click β†’ open in new tab to view full-size.

πŸ—‚οΈ Architecture diagrams

Rendered from the README's Mermaid sources. Click any to open the source SVG full-size.

A insert into source table b mv trigger
A insert into source table b mv trigger
Q1 is the table append only br analytica
Q1 is the table append only br analytica

πŸ“š ClickHouse reference visuals

Curated from the official ClickHouse documentation and engineering blog (and Altinity's docs/blog) β€” the same diagrams the broader CH community uses to explain these concepts. Click any image to read the source.

Anatomy of a MergeTree data part on disk (granules, marks, columns)
ClickHouse Docs Anatomy of a MergeTree data part on disk (granules, marks, columns)
Background merges combine smaller parts into larger sorted parts
ClickHouse Docs Background merges combine smaller parts into larger sorted parts
Inserts produce immutable parts that get merged in the background
ClickHouse Docs Inserts produce immutable parts that get merged in the background
Merge process: decompress, k-way merge, re-index, recompress
ClickHouse Docs Merge process: decompress, k-way merge, re-index, recompress
Sparse primary index: one mark per granule pointing into column files
ClickHouse Docs Sparse primary index: one mark per granule pointing into column files
Granule pruning via the sparse primary index during query execution
ClickHouse Docs Granule pruning via the sparse primary index during query execution

πŸ“š Full module reference

Module 2 β€” Table Engines & Data Modelling

Audience: anyone designing a ClickHouse schema. Prerequisites: Module 1 (parts, merges, MergeTree). Time: ~60 min reading + 30 min hands-on.

By the end you will be able to:


1. The engine landscape at a glance

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "The engine landscape at a glance".

Here is a brief excerpt from the material I'm reading:

"""
| Family       | Use when                                                                                    | Don't use when                            | | **Log**      | Tiny ad-hoc tables. No indexes, no parts. Atomic blocks but no resumable reads.             | Anything you care about losing.           | | **MergeTree**| 99% of analytical tables. Default choice.                                                   | High-write, mutable, single-row workloads.| | **Replicated\***| MergeTree variants on a cluster. Module 4 deep-dives.                                    | Single-node demos....
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                      β”‚             Storage engines            β”‚
                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                     β”‚
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚                             β”‚                                   β”‚
       β–Ό                             β–Ό                                   β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Log     β”‚               β”‚  MergeTree   β”‚                     β”‚ Special   β”‚
  β”‚ family  β”‚               β”‚  family      β”‚                     β”‚ engines   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚                            β”‚                                   β”‚
   β”Œβ”€β”€β”€β”΄β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”
   β”‚        β”‚    β”‚                  β”‚                        β”‚    β”‚          β”‚
  Log    StripeLog              MergeTree              Replicated*    Memory   Buffer
       TinyLog                Replacing                                Dictionary  Distributed
                              Summing                                              Kafka / S3 / URL
                              Aggregating                                          Merge / View / MV
                              Collapsing
                              VersionedCollapsing
                              GraphiteMergeTree
Family Use when Don't use when
Log Tiny ad-hoc tables. No indexes, no parts. Atomic blocks but no resumable reads. Anything you care about losing.
MergeTree 99% of analytical tables. Default choice. High-write, mutable, single-row workloads.
Replicated* MergeTree variants on a cluster. Module 4 deep-dives. Single-node demos.
Memory Caches, ephemeral state, tests. Lost on restart. Anything that must survive a crash.
Buffer Last-resort smoothing layer in front of MergeTree if you cannot batch upstream. If you can batch upstream β€” just batch.
Distributed / Kafka / MySQL / S3 Federation engines. They don't store data β€” they read or fan-out. When you actually want a local table.

2. Plain MergeTree β€” the baseline

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "Plain MergeTree β€” the baseline".

Here is a brief excerpt from the material I'm reading:

"""
(no excerpt β€” section heading was "Plain MergeTree β€” the baseline")
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

You already know it from Module 1. Recap:

Use this until you have a specific reason to pick something else.


3. ReplacingMergeTree β€” slowly-changing dimensions

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "ReplacingMergeTree β€” slowly-changing dimensions".

Here is a brief excerpt from the material I'm reading:

"""
Same as MergeTree, but during merges, **rows with the same sort key are collapsed into one**, keeping the row with the highest *version*. ### Mental model ### Three ways to read | Method                                                                | Latency          | Notes                                                                                       | | `SELECT * FROM t`                                                     | Fast (no merge)  | **Returns dupes** until merge runs. Almost never what you want.                            | | `SELECT * FROM t FINAL`...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

Same as MergeTree, but during merges, rows with the same sort key are collapsed into one, keeping the row with the highest version.

CREATE TABLE users_replacing (
    user_id UInt64,
    name    String,
    email   String,
    version UInt64
)
ENGINE = ReplacingMergeTree(version)
ORDER BY user_id;

Mental model

INSERT batch 1:                  INSERT batch 2:
β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”
β”‚ id=1 β”‚ alice β”‚ a@old    β”‚ v1β”‚  β”‚ id=1 β”‚ alice β”‚ a@new    β”‚ v2β”‚
β”‚ id=2 β”‚ bob   β”‚ b@old    β”‚ v1β”‚  β”‚ id=3 β”‚ carol β”‚ c@new    β”‚ v3β”‚
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”˜

After merge (same id collapses, highest version wins):

  β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”
  β”‚ id=1 β”‚ alice β”‚ a@new    β”‚ v2β”‚
  β”‚ id=2 β”‚ bob   β”‚ b@old    β”‚ v1β”‚
  β”‚ id=3 β”‚ carol β”‚ c@new    β”‚ v3β”‚
  β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”˜

Three ways to read

Method Latency Notes
SELECT * FROM t Fast (no merge) Returns dupes until merge runs. Almost never what you want.
SELECT * FROM t FINAL Slow (merges at read time) Correct, but reads the whole partition. Avoid in hot paths.
SELECT user_id, argMax(name, version), argMax(email, version), max(version) AS v FROM t GROUP BY user_id Fast The production pattern. Works regardless of merge state.

Key takeaway: ReplacingMergeTree gives eventual dedup. If your query absolutely must see at most one row per key, do the dedup in the query (argMax), not via FINAL.


4. SummingMergeTree β€” pre-aggregated counters

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "SummingMergeTree β€” pre-aggregated counters".

Here is a brief excerpt from the material I'm reading:

"""
Like MergeTree, but during merges, **rows with the same sort key get their numeric columns summed**. Non-numeric columns from the row with the *lowest* part get kept (treat them as the dimensions). ### Mental model > **Read pattern:** still wrap in `sum(value)` / `sum(count)` even after > SummingMergeTree, because the merge is incomplete until the next merge > pass. The engine guarantees correct results *if you sum at read time*.
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

Like MergeTree, but during merges, rows with the same sort key get their numeric columns summed. Non-numeric columns from the row with the lowest part get kept (treat them as the dimensions).

CREATE TABLE metrics_summing (
    metric_date Date,
    metric      LowCardinality(String),
    region      LowCardinality(String),
    value       UInt64,           -- summed
    count       UInt64 DEFAULT 1  -- summed
)
ENGINE = SummingMergeTree((value, count))
ORDER BY (metric_date, metric, region);

Mental model

INSERT 1:                              INSERT 2:
(date, m, region, value, count)        (date, m, region, value, count)
('2026-05-01', 'cpu', 'us', 50, 1)    ('2026-05-01', 'cpu', 'us', 30, 1)
('2026-05-01', 'cpu', 'us', 20, 1)    ('2026-05-01', 'cpu', 'eu', 80, 1)

  After merge (sort-key duplicates summed):

  ('2026-05-01', 'cpu', 'us', 100, 3)
  ('2026-05-01', 'cpu', 'eu',  80, 1)

Read pattern: still wrap in sum(value) / sum(count) even after SummingMergeTree, because the merge is incomplete until the next merge pass. The engine guarantees correct results if you sum at read time.


5. AggregatingMergeTree β€” non-additive aggregations

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "AggregatingMergeTree β€” non-additive aggregations".

Here is a brief excerpt from the material I'm reading:

"""
For aggregations that aren't simple sums (`uniq`, `quantile`, `avg`, `max`), `Sum` won't work. Aggregating stores **partial aggregation states** (`AggregateFunction(...)`) and finalises them at read time with `*Merge`. > **Why not just store final values?** Because then you can't roll > *across* buckets. `uniq` over (Mon βˆͺ Tue) β‰  `uniq(Mon) + uniq(Tue)`. > Storing the state lets the engine merge correctly.
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

For aggregations that aren't simple sums (uniq, quantile, avg, max), Sum won't work. Aggregating stores partial aggregation states (AggregateFunction(...)) and finalises them at read time with *Merge.

CREATE TABLE events_agg (
    bucket_date       Date,
    country           LowCardinality(String),
    uniq_users_state  AggregateFunction(uniq, UInt32),
    revenue_state     AggregateFunction(sum, Float64),
    p99_state         AggregateFunction(quantileTDigest(0.99), Float64)
)
ENGINE = AggregatingMergeTree
ORDER BY (bucket_date, country);

-- Insert STATES (not raw values) β€” usually fed by a Materialized View:
INSERT INTO events_agg
SELECT
    bucket_date, country,
    uniqState(user_id),
    sumState(revenue),
    quantileTDigestState(0.99)(latency)
FROM raw_events
GROUP BY bucket_date, country;

-- Read: finalize with *Merge.
SELECT bucket_date, country,
       uniqMerge(uniq_users_state) AS uniq_users,
       sumMerge(revenue_state)     AS revenue,
       quantileTDigestMerge(0.99)(p99_state) AS p99
FROM events_agg
GROUP BY bucket_date, country;

Why not just store final values? Because then you can't roll across buckets. uniq over (Mon βˆͺ Tue) β‰  uniq(Mon) + uniq(Tue). Storing the state lets the engine merge correctly.


6. CollapsingMergeTree β€” current-state mutations

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "CollapsingMergeTree β€” current-state mutations".

Here is a brief excerpt from the material I'm reading:

"""
Designed for "delete or update by writing two rows": one with `sign = +1` inserts, one with `sign = -1` cancels. ### How collapse works ### Two reading patterns > **Gotcha:** if `sign = -1` arrives without the matching `sign = +1` > earlier, you get a *negative* row. The +1/-1 pair must be ordered with > -1 *after* the +1 it cancels.
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

Designed for "delete or update by writing two rows": one with sign = +1 inserts, one with sign = -1 cancels.

CREATE TABLE orders_collapsing (
    order_id UInt64,
    status   LowCardinality(String),
    total    Float64,
    sign     Int8
)
ENGINE = CollapsingMergeTree(sign)
ORDER BY order_id;

INSERT INTO orders_collapsing VALUES (101, 'pending', 10.0, +1);
-- Order 101 paid: cancel old row, write new row in one INSERT
INSERT INTO orders_collapsing VALUES
    (101, 'pending', 10.0, -1),
    (101, 'paid',    10.0, +1);

How collapse works

Before merge:                     After merge:
β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”
β”‚101 β”‚pending β”‚10.0 β”‚+1 β”‚  ──►    β”‚101 β”‚paid   β”‚10.0  β”‚+1 β”‚
β”‚101 β”‚pending β”‚10.0 β”‚-1 β”‚         β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”˜
β”‚101 β”‚paid    β”‚10.0 β”‚+1 β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”˜

Two reading patterns

-- Pattern A: trust the merge (eventual consistency)
SELECT * FROM orders_collapsing FINAL;

-- Pattern B: do it in the query (works pre-merge too)
SELECT order_id,
       argMax(status, sign) AS status,
       sum(total * sign)    AS total
FROM orders_collapsing
GROUP BY order_id;

Gotcha: if sign = -1 arrives without the matching sign = +1 earlier, you get a negative row. The +1/-1 pair must be ordered with -1 after the +1 it cancels.


7. VersionedCollapsingMergeTree β€” out-of-order tolerant

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "VersionedCollapsingMergeTree β€” out-of-order tolerant".

Here is a brief excerpt from the material I'm reading:

"""
CollapsingMergeTree breaks if events arrive out of order. **VersionedCollapsing** adds a `version` column; the engine resolves on `(sort_key, version)`. The cancel rows can arrive *before* the inserts they cancel; the engine sorts them out at merge time using `version`. > **Use this** in any pipeline where event ordering isn't guaranteed > (Kafka, multi-region producers, retry queues).
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

CollapsingMergeTree breaks if events arrive out of order. VersionedCollapsing adds a version column; the engine resolves on (sort_key, version).

CREATE TABLE orders_versioned (
    order_id UInt64,
    status   LowCardinality(String),
    total    Float64,
    version  UInt64,    -- monotonic per order_id
    sign     Int8
)
ENGINE = VersionedCollapsingMergeTree(sign, version)
ORDER BY order_id;

The cancel rows can arrive before the inserts they cancel; the engine sorts them out at merge time using version.

Use this in any pipeline where event ordering isn't guaranteed (Kafka, multi-region producers, retry queues).


8. Log-family engines β€” when minimalism wins

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "Log-family engines β€” when minimalism wins".

Here is a brief excerpt from the material I'm reading:

"""
| Engine    | Atomic INSERT | Indexes | Compressed | Concurrent reads | Use it for                      | | `Log`     | yes (block)   | no      | yes        | yes              | small append-only logs (audit)  | | `TinyLog` | no            | no      | yes        | one at a time    | scratch tables (≀ 1M rows)      | | `StripeLog` | no          | no      | yes        | yes              | medium tables; deprecated path  | There's no `OPTIMIZE`, no merges, no skip-indexes. The engine is just "appendable file". **Don't use Log-family for anything important.**
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
Engine Atomic INSERT Indexes Compressed Concurrent reads Use it for
Log yes (block) no yes yes small append-only logs (audit)
TinyLog no no yes one at a time scratch tables (≀ 1M rows)
StripeLog no no yes yes medium tables; deprecated path

There's no OPTIMIZE, no merges, no skip-indexes. The engine is just "appendable file". Don't use Log-family for anything important.


9. Memory engine β€” RAM-only

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "Memory engine β€” RAM-only".

Here is a brief excerpt from the material I'm reading:

"""
- Stored in process memory only. **Lost on restart.** - Reads are fast; useful for caches, joins' right-hand side, or staging. - No size cap by default β€” easy to OOM the server.
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
CREATE TABLE tmp_uploads (id UUID, payload String) ENGINE = Memory;

10. Buffer engine β€” last-resort write smoothing

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "Buffer engine β€” last-resort write smoothing".

Here is a brief excerpt from the material I'm reading:

"""
A `Buffer` table sits in front of a real MergeTree and accumulates rows in RAM until thresholds trigger a flush. > **Strong recommendation:** prefer batching upstream. A Buffer table is > a band-aid that *loses data on crash* β€” its rows haven't been written > to MergeTree yet. Use it only when the producer literally cannot batch. `OPTIMIZE TABLE facts_buffer;` flushes manually. Reading from the Buffer table reads its in-memory layers + the destination table.
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

A Buffer table sits in front of a real MergeTree and accumulates rows in RAM until thresholds trigger a flush.

CREATE TABLE facts_dest (...) ENGINE = MergeTree ORDER BY (...);

CREATE TABLE facts_buffer AS facts_dest
ENGINE = Buffer(default, facts_dest,
    16,            -- num_layers
    10, 60,        -- min/max time (seconds)
    10000, 1_000_000,    -- min/max rows
    10_000_000, 100_000_000); -- min/max bytes

Strong recommendation: prefer batching upstream. A Buffer table is a band-aid that loses data on crash β€” its rows haven't been written to MergeTree yet. Use it only when the producer literally cannot batch.

OPTIMIZE TABLE facts_buffer; flushes manually. Reading from the Buffer table reads its in-memory layers + the destination table.


11. Materialized Views β€” the "computed table" pattern

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "Materialized Views β€” the "computed table" pattern".

Here is a brief excerpt from the material I'm reading:

"""
In ClickHouse a Materialized View is **not a virtual view**. It's a real table fed by a trigger that runs at INSERT time on a *source table*. | Property                          | Implication                                                                | | Triggers on **INSERT**, not UPDATE | Mutations don't propagate. Drop+recreate the MV to re-aggregate.          | | One MV reads, multiple MVs allowed | A single source row can feed N MVs simultaneously.                        | | `TO <table>` is the modern form    | Decouples MV definition from the storage engine. Always prefer it....
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

In ClickHouse a Materialized View is not a virtual view. It's a real table fed by a trigger that runs at INSERT time on a source table.

CREATE TABLE events_src (
    ts DateTime, user_id UInt64,
    event_type LowCardinality(String), revenue Float64
) ENGINE = MergeTree ORDER BY (event_type, ts);

CREATE TABLE events_per_minute (
    bucket DateTime, event_type LowCardinality(String),
    events UInt64, revenue Float64
) ENGINE = SummingMergeTree ORDER BY (bucket, event_type);

CREATE MATERIALIZED VIEW events_per_minute_mv TO events_per_minute AS
SELECT
    toStartOfMinute(ts) AS bucket, event_type,
    count() AS events, sum(revenue) AS revenue
FROM events_src
GROUP BY bucket, event_type;
Property Implication
Triggers on INSERT, not UPDATE Mutations don't propagate. Drop+recreate the MV to re-aggregate.
One MV reads, multiple MVs allowed A single source row can feed N MVs simultaneously.
TO <table> is the modern form Decouples MV definition from the storage engine. Always prefer it.
MV must GROUP BY for *MergeTree variants Otherwise the engine can't collapse correctly.

Backfill pattern: INSERT INTO events_per_minute SELECT … FROM events_src GROUP BY … after creating the MV β€” the MV won't see old rows on its own.


12. Nested type β€” 1-to-many without joins

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "Nested type β€” 1-to-many without joins".

Here is a brief excerpt from the material I'm reading:

"""
`Nested` stores a "table inside a row" as parallel arrays. Two equivalent reading styles: > **Prefer Nested over JOINs** for tightly-bound 1-to-many like > order-lines, page-impressions-per-session, or telemetry dimensions. > JOINs in CH cost more than in OLTP databases.
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

Nested stores a "table inside a row" as parallel arrays.

CREATE TABLE invoices (
    invoice_id UInt64,
    customer   String,
    total      Decimal(12, 2),
    line_items Nested(
        sku   String,
        qty   UInt32,
        price Decimal(10, 2)
    )
) ENGINE = MergeTree ORDER BY invoice_id;

INSERT INTO invoices VALUES
    (1001, 'Alice', 49.97,
     ['SKU-A','SKU-B','SKU-C'], [1, 2, 1], [9.99, 14.99, 10.00]);

Two equivalent reading styles:

-- Dot-notation: get parallel arrays
SELECT invoice_id,
       line_items.sku, line_items.qty, line_items.price,
       arraySum(arrayMap((q, p) -> q * p, line_items.qty, line_items.price)) AS computed
FROM invoices;

-- ARRAY JOIN: explode to one row per item
SELECT invoice_id, sku, qty, price
FROM invoices
ARRAY JOIN line_items.sku AS sku, line_items.qty AS qty, line_items.price AS price;

Prefer Nested over JOINs for tightly-bound 1-to-many like order-lines, page-impressions-per-session, or telemetry dimensions. JOINs in CH cost more than in OLTP databases.


13. The hands-on demo

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "The hands-on demo".

Here is a brief excerpt from the material I'm reading:

"""
### What you get ### Execution flow β€” what runs, in order | #  | Step          | What happens                                                                                                                                                                                              | | 0  | self-bootstrap | If `m2-clickhouse` isn't healthy, run `up.sh` (which evicts other demo modules first to avoid port conflicts).                                                                                          | | 1  | `setup.sql`   | Creates database `m2` and seven tables, one per engine: `users_rep...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

What you get

docker-compose.yml          m2-clickhouse Β· ports 8123/9000
configs/clickhouse-config.xml
setup.sql Β· data.sql Β· queries.sql Β· extras.sql
up.sh Β· run.sh Β· down.sh

Execution flow β€” what runs, in order

# Step What happens
0 self-bootstrap If m2-clickhouse isn't healthy, run up.sh (which evicts other demo modules first to avoid port conflicts).
1 setup.sql Creates database m2 and seven tables, one per engine: users_replacing (Replacing), metrics_summing (Summing), events_agg (Aggregating with uniq/sum/quantileTDigest states), orders_collapsing (Collapsing), audit_log (Log), tmp_uploads (Memory), facts_dest + facts_buffer (MergeTree + Buffer in front).
2 data.sql Loads each engine with shape-appropriate data: dupes for Replacing, partial counters for Summing, 200k aggregate STATES for Aggregating, +1/-1 sign rows for Collapsing, a few rows for Log/Memory/Buffer.
3 queries.sql Walks each engine's reading pattern: dedup via argMax, OPTIMIZE FINAL reductions, *Merge finalizers for Aggregating, the +1/-1 collapse, and a Buffer flush via OPTIMIZE TABLE m2.facts_buffer to push rows down to facts_dest.
4 extras.sql Three more engines/patterns: VersionedCollapsingMergeTree (out-of-order +/-1 resolved by version), a complete Materialized View pipeline (events_src β†’ events_per_minute_mv β†’ events_per_minute SummingMergeTree, with 200k synthetic events), and the Nested type (invoices.line_items.{sku, qty, price}) queried with both dot-notation and ARRAY JOIN.

14. Decision tree

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "Decision tree".

Here is a brief excerpt from the material I'm reading:

"""
(no excerpt β€” section heading was "Decision tree")
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.

15. Common pitfalls

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "Common pitfalls".

Here is a brief excerpt from the material I'm reading:

"""
| Symptom                                                                            | Cause                                                                              | Fix                                                                                            | | `SELECT * FROM users_replacing` returns duplicates                                 | Merge hasn't run yet.                                                              | Use `argMax` query pattern; never rely on background merge for correctness.                    | | `SELECT *` from SummingMergeTree returns multiple rows per k...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
Symptom Cause Fix
SELECT * FROM users_replacing returns duplicates Merge hasn't run yet. Use argMax query pattern; never rely on background merge for correctness.
SELECT * from SummingMergeTree returns multiple rows per key Same β€” pre-merge state. Always wrap with sum(...) at read time.
Negative rows in CollapsingMergeTree sign=-1 arrived without matching sign=+1; or wrong sort key. Verify your producer always emits +1 then -1; or switch to VersionedCollapsing.
AggregatingMergeTree rows look like garbage Forgot to *Merge at read time, or stored values instead of *State. Insert xxxState(...), read xxxMerge(...).
Buffer table silently drops rows on container restart Buffer is in RAM. Crash = data loss. Don't use Buffer; batch upstream.
Materialized View misses historical data MV only triggers on INSERTs after creation. Backfill: INSERT INTO target SELECT … FROM source GROUP BY ….
MV creates tons of tiny parts Source has small frequent inserts. Add a buffer or batch the source.

16. Talking points for the live session

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "Talking points for the live session".

Here is a brief excerpt from the material I'm reading:

"""
1. **MergeTree is the answer 80% of the time.** Show the decision tree and walk people through it. 2. **`FINAL` is a debugging convenience, not a production pattern.** Demonstrate by running `argMax` vs `FINAL` on the demo's `users_replacing` and showing query duration. 3. **Storing aggregation *state* unlocks rollups.** Show `uniqState` β†’ `uniqMerge` over a week's data; `sum`-of-`uniq` would be wrong. 4. **Materialized Views are write-time triggers, not query-time views.** This is the single biggest ClickHouse misconception. 5. **Nested vs JOIN:** show the same query both ways, time them. Nes...
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
  1. MergeTree is the answer 80% of the time. Show the decision tree and walk people through it.
  2. FINAL is a debugging convenience, not a production pattern. Demonstrate by running argMax vs FINAL on the demo's users_replacing and showing query duration.
  3. Storing aggregation state unlocks rollups. Show uniqState β†’ uniqMerge over a week's data; sum-of-uniq would be wrong.
  4. Materialized Views are write-time triggers, not query-time views. This is the single biggest ClickHouse misconception.
  5. Nested vs JOIN: show the same query both ways, time them. Nested usually wins by 5–20Γ—.

17. Container ports

πŸ’¬ Discuss with AI β€” click to view prompt
Paste into ChatGPT, Claude, Gemini, or any LLM chat.
I'm studying ClickHouse and working through the module "ClickHouse Table Engines". I want to deeply understand the section: "Container ports".

Here is a brief excerpt from the material I'm reading:

"""
| Service        | Container port | Host port | | HTTP           | 8123           | 8123      | | Native (TCP)   | 9000           | 9000      |
"""

Please explain this concept in depth. Cover:

1. **Core mechanics** β€” how does it actually work under the hood? What data structures, algorithms, or engine internals are involved? Where is the implementation in the ClickHouse source tree (file/folder names) if you know?

2. **When to use vs avoid** β€” what concrete workloads does this help, and when would it hurt or be wrong? Give specific scale/latency trade-offs.

3. **Comparable features in other systems** β€” how does this compare with similar features in PostgreSQL, MySQL, BigQuery, Snowflake, Redshift, or DuckDB? Build my intuition by analogy.

4. **Common pitfalls and gotchas** β€” what trips engineers up in production? Subtle bugs, performance traps, surprising semantics?

5. **Worked example** β€” show me a small but realistic SQL or config example that demonstrates the concept end-to-end. Include the expected output where useful.

6. **Where to read more** β€” a short list of authoritative pointers (CH docs, blog posts, talks, source-code files).

Assume I have solid SQL fundamentals but I'm new to ClickHouse internals. Be technical but pragmatic; avoid marketing language.
Service Container port Host port
HTTP 8123 8123
Native (TCP) 9000 9000

πŸ“Š ClickHouse Knowledge Transfer

Module 2 of 10 | Duration: 6-8 weeks total

Created for comprehensive ClickHouse training | 2026

← Previous: Module 1 - Fundamentals 🏠 Home Next: Module 3 - Sharding β†’