Understanding the Table API
Deephaven's Table API lets you filter, transform, join, and aggregate data using a consistent set of operations. This guide explains the core concepts behind the API, helping you write more effective queries and avoid common pitfalls.
Note
New to Deephaven? Start with How Deephaven works: A mental model for the conceptual foundation this guide builds on. This page assumes you already know why tables don't copy data and why formulas run in the engine, and focuses on the API surface and tradeoffs you'll actually choose between.
For a quick-reference index of available operations and where to find full guides for each, see Table operations overview.
Tables are immutable
Table operations never modify their source — the original is always safe to keep using. Most operations return a distinct new table; a no-op transformation may occasionally hand back the same object as an optimization, but either way the source is left untouched:
This differs from some data libraries where operations modify data in place. In Deephaven, you build up results by chaining operations, and each step leaves its input untouched. That result usually isn't a full copy, though — see Tables are recipes, not data for how the engine shares unchanged columns instead of duplicating them.
Why immutability matters:
- Debugging: Intermediate tables remain available for inspection.
- Reuse: The same source can feed multiple downstream operations without interference.
- Live updates: The engine can safely propagate changes through the dependency graph.
Formulas are strings
Column expressions are written as strings:
These strings are parsed and executed by Deephaven's Java engine. This has several implications:
Syntax is Java-like:
- Use
Math.sqrtfor math functions. - String literals use backticks:
`hello`. - Ternary expressions:
X > 0 ? X : -X.
You can call Groovy closures, but there's a consideration:
The engine calls your closure once per row — see Calling Groovy from formulas for the details. For performance-critical code, prefer built-in functions.
Query scope makes variables available:
Variables defined in the script are automatically available in formula strings through Deephaven's query scope.
What's available inside formulas
Inside a formula string, you have access to:
Column values — Reference by name:
Special variables:
i— Row position asint(0, 1, 2, ...)ii— Row position aslong(for tables with more than 2 billion rows)k— Internal row key (use cautiously; not the same as row position)
i/ii are valid on static, append-only, or blink tables; k is valid on a slightly broader set — static, add-only (which includes append-only), or blink tables (see Table types for what these mean). A general refreshing table rejects whichever of these it doesn't satisfy, because positions and keys can shift. See special variables for the full compatibility matrix.
Built-in functions — Math, string manipulation, time operations:
Query scope variables and your own Groovy closures are also available inside formulas — see Formulas are strings above for how those work and their tradeoffs.
Operations build a dependency graph
When you chain operations, you create a directed acyclic graph (DAG) of table dependencies:
For static tables, this is just a convenient way to structure code.
For live (refreshing) tables, the graph becomes active:
- When source data changes, updates propagate automatically through all dependent tables.
- You don't re-run your code — the engine handles incremental updates.
- Each downstream table sees a consistent view of the data.
This is why Deephaven can efficiently process real-time data: it typically recomputes only what changed, not the entire result.
Memory vs computation tradeoffs
The Table API offers several ways to add columns, each with different performance characteristics:
| Operation | Stores values | Recomputes on access | Best for |
|---|---|---|---|
update | Yes | No | Expensive formulas, values accessed repeatedly |
view | No | Yes | Simple formulas, memory-constrained environments |
select | Yes | No | Creating a new table with only specific columns |
updateView | No | Yes | Same as view, but keeping all original columns |
lazyUpdate | Cached | When cache misses | Few unique input values, expensive computation |
update computes values once and stores them:
view computes on demand:
select is like update but only includes specified columns:
For refreshing tables, this choice also affects update performance. view recomputes on every access, while update recomputes only when source data changes.
Same API, different behavior
The same operations work on both static and live tables — see Static vs. live: understanding mutability for the underlying concept. In practice, this means:
The code is identical. The difference:
staticResultis computed once and never changes.liveResultautomatically updates as new rows arrive inliveTable.
You can check whether a table is live with isRefreshing:
Related documentation
- How Deephaven works: A mental model — The conceptual foundation this guide builds on
- Table operations overview — Quick-reference index of available operations
- Deephaven's design — Architecture and update model
- Table types — Static, streaming, blink, and ring tables
- Javadoc: Table — Complete Table interface
- Javadoc: TableOperations — Operation contracts
How table operations work in Deephaven