Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion documentation/query/datatypes/array.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,9 @@ SELECT ARRAY[1, NULL, 3] = ARRAY[1, NULL, 3] as with_nulls;
## Array functions

Functions that operate on arrays are documented on a
[dedicated page](/docs/query/functions/array). There are also some
[dedicated page](/docs/query/functions/array), including
[element-wise aggregates](/docs/query/functions/array/#array_elem_min) that
work with `GROUP BY` and `SAMPLE BY`. There are also some
[financial functions](/docs/query/functions/finance#l2price) that operate on
arrays.

Expand Down
15 changes: 15 additions & 0 deletions documentation/query/functions/aggregation.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,21 @@ calculations. Functions are organized by category below.
| [weighted_stddev_freq](#weighted_stddev_freq) | Weighted standard deviation (frequency weights) |
| [weighted_stddev_rel](#weighted_stddev_rel) | Weighted standard deviation (reliability weights) |

### Array aggregates

Several aggregates on this page ([first](#first), [first_not_null](#first_not_null),
[last](#last), [last_not_null](#last_not_null)) accept array columns directly.
For element-wise aggregation across arrays — summing, averaging, or finding
min/max position-by-position — see the
[array functions](/docs/query/functions/array/) page.

| Function | Description |
| :------- | :---------- |
| [array_elem_avg](/docs/query/functions/array/#array_elem_avg) | Element-wise average across arrays |
| [array_elem_max](/docs/query/functions/array/#array_elem_max) | Element-wise maximum across arrays |
| [array_elem_min](/docs/query/functions/array/#array_elem_min) | Element-wise minimum across arrays |
| [array_elem_sum](/docs/query/functions/array/#array_elem_sum) | Element-wise sum across arrays |

---

QuestDB supports implicit `GROUP BY`. When aggregate functions are used with
Expand Down
277 changes: 277 additions & 0 deletions documentation/query/functions/array.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,283 @@ SELECT array_cum_sum(ARRAY[ [1.0, 1.0], [2.0, 2.0] ]);
| ---------------------- |
| ARRAY[1.0,2.0,4.0,6.0] |

## array_elem_min

`array_elem_min(array1, array2 [, ...])` or `array_elem_min(array)` returns an
array where each element is the minimum of the corresponding elements across the
inputs. Works in two modes:

- **Multi-argument (per-row):** pass two or more `DOUBLE[]` expressions. The
function returns a single array that is the element-wise minimum of all
arguments.
- **Aggregate (GROUP BY / SAMPLE BY):** pass a single `DOUBLE[]` column. The
function aggregates across rows, returning one result array per group.

`NULL` arrays are skipped entirely. `NULL` elements within an array are skipped
at that position. If every input at a given position is `NULL`, the result at
that position is `NULL`.

When input arrays have different lengths, the output length is the maximum
across all inputs. Positions beyond the end of a shorter array receive no
contribution from that array.

N-dimensional arrays are supported. The output shape is the per-dimension
maximum of all inputs. In multi-argument mode, all arguments must have the same
number of dimensions.

#### Parameters

**Multi-argument mode:**

- `array1` — a `DOUBLE[]` array
- `array2` — a `DOUBLE[]` array
- `...` — additional `DOUBLE[]` arrays (optional)

**Aggregate mode:**

- `array` — a `DOUBLE[]` column

#### Examples

**Multi-argument — element-wise minimum of two arrays:**

```questdb-sql
SELECT array_elem_min(ARRAY[1.0, 5.0, 3.0], ARRAY[4.0, 2.0, 6.0]);
```

| array_elem_min |
| --------------- |
| [1.0,2.0,3.0] |

**Multi-argument — arrays of different lengths:**

```questdb-sql
SELECT array_elem_min(
ARRAY[100.0, 200.0, 150.0],
ARRAY[120.0, 180.0, 160.0, 90.0]
);
```

| array_elem_min |
| --------------------------- |
| [100.0,180.0,150.0,90.0] |

The fourth position has only one contributing value.

**Multi-argument — NULL elements are skipped:**

```questdb-sql
SELECT array_elem_min(ARRAY[100.0, null], ARRAY[null, 200.0]);
```

| array_elem_min |
| -------------- |
| [100.0,200.0] |

Each position takes the minimum over the values that are present (1 value each
here, not 2).

**Aggregate — worst bid prices per symbol each hour:**

```questdb-sql
CREATE TABLE book (
ts TIMESTAMP, symbol SYMBOL, bid_prices DOUBLE[]
) TIMESTAMP(ts) PARTITION BY DAY;

INSERT INTO book VALUES
('2025-06-01T09:30:00', 'AAPL', ARRAY[150.0, 149.5, 149.0]),
('2025-06-01T09:30:05', 'AAPL', ARRAY[150.5, 149.0, 148.5, 148.0]),
('2025-06-01T09:30:00', 'MSFT', ARRAY[420.0, 419.5]),
('2025-06-01T09:30:05', 'MSFT', ARRAY[419.0, 418.5]);

SELECT ts, symbol, array_elem_min(bid_prices)
FROM book
SAMPLE BY 1h;
```

| ts | symbol | array_elem_min |
| --------------------------- | ------ | -------------------------- |
| 2025-06-01T09:00:00.000000Z | AAPL | [150.0,149.0,148.5,148.0] |
| 2025-06-01T09:00:00.000000Z | MSFT | [419.0,418.5] |

The `AAPL` group has snapshots with different book depths. The fourth position
only has one contributing row.

**Multi-argument — 2D arrays:**

```questdb-sql
SELECT array_elem_min(
ARRAY[[1.0, 8.0, 3.0], [5.0, 2.0, 9.0]],
ARRAY[[4.0, 6.0, 7.0], [3.0, 8.0, 1.0]]
);
```

| array_elem_min |
| ---------------------------------- |
| [[1.0,6.0,3.0],[3.0,2.0,1.0]] |

## array_elem_max

`array_elem_max(array1, array2 [, ...])` or `array_elem_max(array)` returns an
array where each element is the maximum of the corresponding elements across the
inputs. Works in both
[multi-argument and aggregate modes](#array_elem_min), with the same NULL handling,
different-length, and multi-dimensional behavior as `array_elem_min`.

#### Parameters

**Multi-argument mode:**

- `array1`, `array2` [, `...`] — two or more `DOUBLE[]` arrays

**Aggregate mode:**

- `array` — a `DOUBLE[]` column

#### Examples

```questdb-sql
SELECT array_elem_max(ARRAY[1.0, 5.0, 3.0], ARRAY[4.0, 2.0, 6.0]);
```

| array_elem_max |
| --------------- |
| [4.0,5.0,6.0] |

```questdb-sql
SELECT array_elem_max(
ARRAY[[1.0, 8.0, 3.0], [5.0, 2.0, 9.0]],
ARRAY[[4.0, 6.0, 7.0], [3.0, 8.0, 1.0]]
);
```

| array_elem_max |
| ---------------------------------- |
| [[4.0,8.0,7.0],[5.0,8.0,9.0]] |

**Aggregate — best bid prices per symbol each hour:**

Using the `book` table from the [array_elem_min](#array_elem_min) example:

```questdb-sql
SELECT ts, symbol, array_elem_max(bid_prices)
FROM book
SAMPLE BY 1h;
```

| ts | symbol | array_elem_max |
| --------------------------- | ------ | -------------------------- |
| 2025-06-01T09:00:00.000000Z | AAPL | [150.5,149.5,149.0,148.0] |
| 2025-06-01T09:00:00.000000Z | MSFT | [420.0,419.5] |

## array_elem_sum

`array_elem_sum(array1, array2 [, ...])` or `array_elem_sum(array)` returns an
array where each element is the sum of the corresponding elements across the
inputs. Works in both
[multi-argument and aggregate modes](#array_elem_min), with the same NULL handling,
different-length, and multi-dimensional behavior as `array_elem_min`.

Uses Kahan compensated summation to minimize floating-point rounding errors.

#### Parameters

**Multi-argument mode:**

- `array1`, `array2` [, `...`] — two or more `DOUBLE[]` arrays

**Aggregate mode:**

- `array` — a `DOUBLE[]` column

#### Examples

```questdb-sql
SELECT array_elem_sum(
ARRAY[1.0, 2.0, 3.0],
ARRAY[10.0, 20.0, 30.0]
);
```

| array_elem_sum |
| ----------------- |
| [11.0,22.0,33.0] |

**Aggregate — total filled quantities per level across a session:**

```questdb-sql
CREATE TABLE fills (
ts TIMESTAMP, symbol SYMBOL, fill_qtys DOUBLE[]
) TIMESTAMP(ts) PARTITION BY DAY;

INSERT INTO fills VALUES
('2025-06-01T09:30:00', 'AAPL', ARRAY[100.0, 200.0]),
('2025-06-01T09:30:05', 'AAPL', ARRAY[150.0, 50.0]),
('2025-06-01T09:30:10', 'AAPL', ARRAY[250.0, 300.0]);

SELECT array_elem_sum(fill_qtys) FROM fills;
```

| array_elem_sum |
| --------------- |
| [500.0,550.0] |

## array_elem_avg

`array_elem_avg(array1, array2 [, ...])` or `array_elem_avg(array)` returns an
array where each element is the average of the corresponding elements across the
inputs. Works in both
[multi-argument and aggregate modes](#array_elem_min), with the same NULL handling,
different-length, and multi-dimensional behavior as `array_elem_min`.

Uses Kahan compensated summation to minimize floating-point rounding errors.

#### Parameters

**Multi-argument mode:**

- `array1`, `array2` [, `...`] — two or more `DOUBLE[]` arrays

**Aggregate mode:**

- `array` — a `DOUBLE[]` column

#### Examples

```questdb-sql
SELECT array_elem_avg(ARRAY[10.0, 20.0, 30.0], ARRAY[30.0, 40.0, 50.0]);
```

| array_elem_avg |
| ------------------ |
| [20.0,30.0,40.0] |

**Aggregate — average bid sizes per symbol each hour:**

```questdb-sql
CREATE TABLE book_sizes (
ts TIMESTAMP, symbol SYMBOL, bid_sizes DOUBLE[]
) TIMESTAMP(ts) PARTITION BY DAY;

INSERT INTO book_sizes VALUES
('2025-06-01T09:30:00', 'AAPL', ARRAY[100.0, 200.0, 150.0]),
('2025-06-01T09:30:05', 'AAPL', ARRAY[120.0, 180.0, 160.0, 90.0]),
('2025-06-01T09:30:00', 'MSFT', ARRAY[500.0, 400.0]),
('2025-06-01T09:30:05', 'MSFT', ARRAY[600.0, 300.0]);

SELECT ts, symbol, array_elem_avg(bid_sizes)
FROM book_sizes
SAMPLE BY 1h;
```

| ts | symbol | array_elem_avg |
| --------------------------- | ------ | ------------------------ |
| 2025-06-01T09:00:00.000000Z | AAPL | [110.0,190.0,155.0,90.0] |
| 2025-06-01T09:00:00.000000Z | MSFT | [550.0,350.0] |

The `AAPL` group has snapshots with different book depths. The fourth position
only has one contributing row.

## array_max

`array_max(array)` returns the maximum value from all the array elements. `NULL`
Expand Down