# row

> For the complete documentation index, see [llms.txt](https://docs.redpanda.com/llms.txt). Component-specific: [cloud-data-platform-full.txt](https://docs.redpanda.com/cloud-data-platform-full.txt)

---
title: row
latest-operator-version: v26.1.4
latest-console-tag: v3.7.3
latest-connect-version: 4.93.0
latest-redpanda-tag: v26.1.9
docname: sql/sql-data-types/row
page-component-name: cloud-data-platform
page-version: master
page-component-version: master
page-component-title: Cloud
page-relative-src-path: sql/sql-data-types/row.adoc
page-edit-url: https://github.com/redpanda-data/cloud-docs/edit/main/modules/reference/pages/sql/sql-data-types/row.adoc
description: The `row` data type represents a composite value containing one or more fields of different types.
page-topic-type: reference
page-git-created-date: "2026-05-26"
page-git-modified-date: "2026-05-26"
---

<!-- Source: https://docs.redpanda.com/cloud-data-platform/reference/sql/sql-data-types/row.md -->

The `row` data type represents a composite value (also known as a struct or record) containing one or more fields of different types. `row` values support field access, lexicographic comparison, `NULL` checks, conversion to text, and use in `GROUP BY`, `ORDER BY`, and `JOIN` clauses.

## [](#syntax)Syntax

```sql
ROW(expression [, ...])
(expression, expression [, ...])
```

The explicit `row` keyword is required for single-element composites. For two or more elements, the `row` keyword is optional and the parenthesized list is treated as an implicit tuple.

## [](#examples)Examples

### [](#create-a-row-with-multiple-values)Create a ROW with multiple values

```sql
SELECT ROW(1, 'hello', 3.14);
```

```sql
       row
-----------------
 (1,"hello",3.14)
(1 row)
```

### [](#use-implicit-tuple-syntax)Use implicit tuple syntax

```sql
SELECT (1, 2, 3);
```

```sql
  row
---------
 (1,2,3)
(1 row)
```

### [](#create-a-nested-row)Create a nested ROW

```sql
SELECT ROW(1, ROW(2, 3));
```

```sql
    row
-----------
 (1,"(2,3)")
(1 row)
```

### [](#create-an-empty-row)Create an empty ROW

```sql
SELECT ROW();
```

```sql
 row
-----
 ()
(1 row)
```

## [](#access-fields)Access fields

### [](#access-by-position)Access by position

For anonymous ROW expressions, fields are accessed by the positional names `f1`, `f2`, and so on, in declaration order:

```sql
SELECT (ROW(1, 'hello', 3.14)).f1, (ROW(1, 'hello', 3.14)).f2;
```

```sql
 f1 |  f2
----+-------
  1 | hello
(1 row)
```

The parentheses around the ROW expression are required when accessing a field.

### [](#access-by-name)Access by name

For composite columns with declared field names, for example, columns mapped from a topic with `struct_mapping_policy = 'COMPOUND'` (see [CREATE TABLE](https://docs.redpanda.com/cloud-data-platform/reference/sql/sql-statements/create-table/)), access fields by their declared names:

```sql
SELECT (record).customer_id, (record).order_total FROM orders;
```

### [](#expand-all-fields-with-a-wildcard)Expand all fields with a wildcard

To project every field of a ROW value as a separate result column, use the `.*` form:

```sql
SELECT (ROW(1, 'hello', 3.14)).*;
```

```sql
 f1 |  f2   |  f3
----+-------+------
  1 | hello | 3.14
(1 row)
```

The wildcard form also works inside a `ROW(…​)` constructor to copy fields from one composite into another.

## [](#compare-row-values)Compare ROW values

ROW values support the standard comparison operators `=`, `<>`, `<`, `⇐`, `>`, and `>=`. Comparison is **lexicographic**: fields are compared in order, left to right, and the first differing field determines the result.

```sql
SELECT ROW(1, 'a') < ROW(1, 'b');
```

```sql
 ?column?
----------
 t
(1 row)
```

Both ROW values must have the same number of fields, and corresponding fields must have comparable types.

## [](#check-for-null)Check for NULL

ROW values support `IS NULL` and `IS NOT NULL`, but with semantics that differ from scalar columns:

-   `expression IS NULL` returns `true` when the expression itself is NULL, **or** when all of the row’s fields are NULL.

-   `expression IS NOT NULL` returns `true` when the expression itself is non-NULL **and** all of the row’s fields are non-NULL.


Because of this, `IS NULL` and `IS NOT NULL` are not always inverses for ROW values. Both can return `false` for the same input, such as a ROW with a mix of NULL and non-NULL fields.

```sql
SELECT ROW(1, 'a') IS NULL    AS is_null,
       ROW(1, 'a') IS NOT NULL AS is_not_null;
```

```sql
 is_null | is_not_null
---------+-------------
 f       | t
(1 row)
```

For a ROW with at least one NULL field and at least one non-NULL field, both checks return `false`:

```sql
SELECT ROW(NULL, 'a') IS NULL    AS is_null,
       ROW(NULL, 'a') IS NOT NULL AS is_not_null;
```

```sql
 is_null | is_not_null
---------+-------------
 f       | f
(1 row)
```

> 📝 **NOTE**
>
> These checks do not recurse into nested ROW values. A nested ROW with all-NULL fields counts as a value (not NULL) at the outer level, so the outer `IS NULL` returns `false`. To check a specific nested field directly, access the field and test that.

## [](#convert-to-text)Convert to text

Cast a ROW value to `text` to produce the standard PostgreSQL composite literal form:

```sql
SELECT ROW(1, 'hello', 3.14)::text;
```

```sql
       row
-----------------
 (1,"hello",3.14)
(1 row)
```

## [](#use-row-in-queries)Use ROW in queries

ROW values can be used in `GROUP BY`, `ORDER BY`, and `JOIN` clauses with lexicographic comparison semantics.

### [](#group-by-a-row-field)Group by a ROW field

```sql
SELECT (customer).region, COUNT(*)
FROM orders
GROUP BY (customer).region;
```

### [](#order-by-a-whole-row)Order by a whole ROW

```sql
SELECT * FROM orders ORDER BY customer;
```

The rows are sorted lexicographically by the fields of the `customer` composite column, in their declared order.

### [](#join-on-a-multi-column-key)Join on a multi-column key

Compare implicit tuples to match multi-column keys without spelling out each field in a `WHERE` clause:

```sql
SELECT *
FROM table_a a
JOIN table_b b
ON (a.col1, a.col2) = (b.col1, b.col2);
```

## [](#suggested-reading)Suggested reading

-   [CREATE TABLE](https://docs.redpanda.com/cloud-data-platform/reference/sql/sql-statements/create-table/): Maps a Redpanda topic to a SQL table. Use `struct_mapping_policy = 'COMPOUND'` to surface nested topic fields as user-defined types accessible with ROW field-access syntax.