All Products
Search
Document Center

ApsaraDB for SelectDB:Query complex data types

Last Updated:Jun 26, 2026

ApsaraDB for SelectDB supports complex data types such as ARRAY, MAP, STRUCT, JSON, and VARIANT. These data types are ideal for storing and querying nested and semi-structured data. This topic describes the use cases, table creation methods, and common query functions for each complex data type.

Overview

In real-world business scenarios, data often contains semi-structured information, such as lists of tags, attribute key-value pairs, or nested objects. Traditional relational columns like VARCHAR or INT cannot efficiently represent this data. ApsaraDB for SelectDB provides the following complex data types to address this need:

Data type

Description

Use cases

ARRAY

An ordered collection of elements of the same type.

Tag lists, multi-value attributes, and sequences of scores.

MAP

A collection of key-value pairs, where keys and values have specified types.

Extended attributes, configuration items, and dynamic fields.

STRUCT

A collection of named fields, each with a specific data type.

Address information, coordinates, and nested entities.

JSON

JSON data stored in a binary format.

Semi-structured data with a flexible schema and log events.

VARIANT

A semi-structured data type that automatically infers types and uses columnar storage.

Scenarios with dynamically changing fields, such as log analysis and user profiling.

ARRAY type

The ARRAY type stores an ordered collection of elements of the same type. The declaration syntax is ARRAY<element_type>. Nested arrays, such as ARRAY<ARRAY<INT>>, are also supported.

Create table example

CREATE TABLE user_tags (
    user_id BIGINT,
    tags ARRAY<VARCHAR(50)>,
    scores ARRAY<INT>
)
DUPLICATE KEY(user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 4
PROPERTIES ("replication_allocation" = "tag.location.default: 1");

Insert data

INSERT INTO user_tags VALUES
(1, ['sports', 'music', 'travel'], [90, 85, 78]),
(2, ['tech', 'gaming'], [95, 88]),
(3, ['cooking', 'reading', 'sports', 'photography'], [70, 92, 80, 65]);

Common query functions

Function

Description

Example

array[index]

Accesses an element by its index (0-based).

SELECT tags[0] FROM user_tags

array_size()

Returns the number of elements in the array.

SELECT array_size(tags) FROM user_tags

array_contains()

Checks if the array contains a specified element.

SELECT * FROM user_tags WHERE array_contains(tags, 'sports')

array_sort()

Returns a sorted array.

SELECT array_sort(scores) FROM user_tags

array_distinct()

Returns an array with duplicate elements removed.

SELECT array_distinct(tags) FROM user_tags

array_join()

Joins array elements into a string.

SELECT array_join(tags, ',') FROM user_tags

For more ARRAY functions, see the Apache Doris Array Functions documentation.

Flattening an array

Use the EXPLODE() function with LATERAL VIEW to expand each array element into a separate row.

SELECT user_id, tag
FROM user_tags
LATERAL VIEW EXPLODE(tags) tmp AS tag;

MAP type

The MAP type stores a collection of key-value pairs. The declaration syntax is MAP<key_type, value_type>. The key must be a primitive type, while the value can be a nested complex data type.

Create table example

CREATE TABLE product_attrs (
    product_id BIGINT,
    attributes MAP<VARCHAR(50), VARCHAR(200)>
)
DUPLICATE KEY(product_id)
DISTRIBUTED BY HASH(product_id) BUCKETS 4
PROPERTIES ("replication_allocation" = "tag.location.default: 1");

Insert data

INSERT INTO product_attrs VALUES
(1001, {'color': 'red', 'size': 'XL', 'material': 'cotton'}),
(1002, {'color': 'blue', 'weight': '500g'}),
(1003, {'brand': 'Acme', 'origin': 'China', 'warranty': '2 years'});

Common query functions

Function

Description

Example

map['key']

Retrieves a value by its key.

SELECT attributes['color'] FROM product_attrs

map_size()

Returns the number of key-value pairs.

SELECT map_size(attributes) FROM product_attrs

map_keys()

Returns an array of all keys.

SELECT map_keys(attributes) FROM product_attrs

map_values()

Returns an array of all values.

SELECT map_values(attributes) FROM product_attrs

map_contains_key()

Checks if the map contains a specified key.

SELECT * FROM product_attrs WHERE map_contains_key(attributes, 'brand')

For more MAP functions, see the Apache Doris Map Functions documentation.

Flattening a map

SELECT product_id, key, value
FROM product_attrs
LATERAL VIEW EXPLODE_MAP(attributes) tmp AS key, value;

STRUCT type

The STRUCT type consists of a set of named fields, each of which can have a different data type. The declaration syntax is STRUCT<field1:type1, field2:type2, ...>.

Create table example

CREATE TABLE orders (
    order_id BIGINT,
    address STRUCT<city:VARCHAR(50), street:VARCHAR(200), zipcode:VARCHAR(10)>
)
DUPLICATE KEY(order_id)
DISTRIBUTED BY HASH(order_id) BUCKETS 4
PROPERTIES ("replication_allocation" = "tag.location.default: 1");

Insert data

INSERT INTO orders VALUES
(1, NAMED_STRUCT('city', 'Beijing', 'street', 'Zhongguancun Road 1', 'zipcode', '100080')),
(2, NAMED_STRUCT('city', 'Shanghai', 'street', 'Nanjing West Road 100', 'zipcode', '200041'));

Query methods

Use dot notation (.) to access fields within a struct:

-- Access fields of a struct
SELECT order_id, address.city, address.zipcode FROM orders;

-- Use a struct field in a WHERE clause
SELECT * FROM orders WHERE address.city = 'Beijing';

JSON type

The JSON type stores JSON data in a binary format and is suitable for semi-structured data scenarios with a flexible schema. Compared to storing a JSON string as VARCHAR, the JSON type provides better query performance because it avoids repeated parsing.

Create table example

CREATE TABLE event_logs (
    event_id BIGINT,
    event_time DATETIME,
    payload JSON
)
DUPLICATE KEY(event_id)
DISTRIBUTED BY HASH(event_id) BUCKETS 4
PROPERTIES ("replication_allocation" = "tag.location.default: 1");

Insert data

INSERT INTO event_logs VALUES
(1, '2024-01-15 10:30:00', '{"action": "login", "device": "mobile", "os": "iOS", "version": "17.2"}'),
(2, '2024-01-15 10:31:00', '{"action": "purchase", "amount": 99.9, "items": ["book", "pen"], "coupon": null}'),
(3, '2024-01-15 10:32:00', '{"action": "logout", "duration_sec": 300}');

Common query functions

Function

Description

Example

json_extract()

Extracts a value using a JSONPath expression.

SELECT json_extract(payload, '$.action') FROM event_logs

json_extract_string()

Extracts a JSON value and returns it as a string.

SELECT json_extract_string(payload, '$.device') FROM event_logs

json_extract_int()

Extracts a JSON value and returns it as an integer.

SELECT json_extract_int(payload, '$.duration_sec') FROM event_logs

json_extract_double()

Extracts a JSON value and returns it as a double.

SELECT json_extract_double(payload, '$.amount') FROM event_logs

json_exists_path()

Checks if a specified path exists.

SELECT * FROM event_logs WHERE json_exists_path(payload, '$.coupon')

json_type()

Returns the type of a JSON value.

SELECT json_type(payload, '$.items') FROM event_logs

For more JSON functions, see the Apache Doris JSON Functions documentation.

Usage notes

  • For frequently queried JSON fields, consider extracting them into separate columns or using the VARIANT type for better query performance.

  • The JSON type cannot be used as a key column or for partitioning and bucketing.

  • The maximum size for a single JSON field is 1 GB.

VARIANT type

VARIANT is a semi-structured data type provided by ApsaraDB for SelectDB that automatically infers the specific data type and stores it in columnar storage. Compared to the JSON type, VARIANT offers superior query performance and is ideal for scenarios that require high-performance analysis on data with variable fields.

Create table example

CREATE TABLE user_events (
    event_id BIGINT,
    event_time DATETIME,
    event_data VARIANT
)
DUPLICATE KEY(event_id)
DISTRIBUTED BY HASH(event_id) BUCKETS 4
PROPERTIES ("replication_allocation" = "tag.location.default: 1");

Insert data

The VARIANT type accepts JSON-formatted strings. The system automatically infers the schema and stores the data in columnar storage.

INSERT INTO user_events VALUES
(1, '2024-01-15 10:00:00', '{"uid": 1001, "action": "click", "page": "/home", "duration": 5.2}'),
(2, '2024-01-15 10:01:00', '{"uid": 1002, "action": "scroll", "page": "/products", "items_viewed": 12}'),
(3, '2024-01-15 10:02:00', '{"uid": 1001, "action": "purchase", "amount": 299.0, "payment": "alipay"}');

Query methods

Access VARIANT fields using dot or bracket notation. You can also use CAST to perform type conversions:

-- Directly access fields (returns a VARIANT type)
SELECT event_data['action'], event_data['page'] FROM user_events;

-- Convert types using CAST
SELECT
    CAST(event_data['uid'] AS BIGINT) AS uid,
    CAST(event_data['action'] AS VARCHAR) AS action,
    CAST(event_data['amount'] AS DOUBLE) AS amount
FROM user_events
WHERE CAST(event_data['action'] AS VARCHAR) = 'purchase';

VARIANT vs. JSON

Dimension

VARIANT

JSON

Storage method

columnar storage with automatic type inference

binary JSON format

Query performance

High (columnar storage and vectorized execution)

Medium (requires runtime parsing)

Schema flexibility

Automatically adapts without a predefined schema

Completely flexible

Recommended use cases

Scenarios requiring high-performance analysis, such as log analysis and user behavior analysis

Scenarios with extremely high write frequency and highly variable fields

Limitations

  • ARRAY, MAP, STRUCT, JSON, and VARIANT types cannot be used as a table's key column (such as columns in a DUPLICATE KEY, UNIQUE KEY, or AGGREGATE KEY).

  • Complex data types cannot be used as bucketing columns in a DISTRIBUTED BY clause.

  • The ARRAY and MAP types can be nested up to 9 levels deep.

  • In an AGGREGATE KEY table, the only supported aggregate functions for complex-type columns are REPLACE_IF_NOT_NULL and NONE.