Basic usage

Updated at:

This topic describes the core DDL syntax, partitioning strategies, and common query patterns for columnstore indexes. All examples run in OLAP Mode by default. The syntax also applies to Lake Mode.

Sample table

The order detail table orders is used as an example, where id is the order identifier and dt is the order date. The two columns form a composite primary key:

+------------+-------------+---------+----------------+
| TABLE_NAME | COLUMN_NAME |  TYPE   | IS_PRIMARY_KEY |
+------------+-------------+---------+----------------+
| orders     | id          | BIGINT  | true           |
| orders     | dt          | VARCHAR | true           |
| orders     | user_id     | BIGINT  | false          |
| orders     | amount      | DECIMAL | false          |
| orders     | status      | VARCHAR | false          |
+------------+-------------+---------+----------------+

Create a columnstore index

Syntax

CREATE INDEX index_name USING COLUMNAR
ON table_name(column_name(,...))
PARTITION BY ENUMERABLE (
  column_name(,...),
  bucket(bucket_num, column_name)
)
WITH (
  `lindorm_columnar.user.index.database` = 'columnar_db_name',
  `lindorm_columnar.user.index.table`    = 'columnar_tbl_name'
);

Parameters

Parameter

Description

index_name

The name of the columnstore index. It can contain uppercase letters, lowercase letters, digits, and underscores (_).

table_name

The name of the wide table.

column_name(,...)

The list of columns for which to create the columnstore index, separated by commas. All primary key columns of the wide table must be included. To create an index on all columns (primary key + non-primary key), you can use the shorthand (*). Supported data types: TINYINT, SMALLINT, INTEGER, BIGINT, LONG, FLOAT, DOUBLE, VARCHAR, BINARY, VARBINARY, BOOLEAN, DECIMAL, JSON, DATE, TIMESTAMP.

PARTITION BY ENUMERABLE(...)

Specifies the partitioning strategy for the index data. For more information, see Partitioning strategies.

WITH(...)

Specifies the storage parameters for the columnstore index as key-value pairs. Common parameters:
lindorm_columnar.user.index.database: The name of the database where the columnstore index table resides.
lindorm_columnar.user.index.table: The name of the columnstore index table.
For other parameters, see Advanced usage and Storage modes.


Example

The following example creates a columnstore index on all columns of the orders table, partitioned by the order date dt with 128 buckets and the bucket partition field as the order identifier id:

CREATE INDEX orders_idx USING COLUMNAR
ON orders(id, dt, user_id, amount, status)
PARTITION BY ENUMERABLE (dt, bucket(128, id))
WITH (
  `lindorm_columnar.user.index.database` = 'my_index_db',
  `lindorm_columnar.user.index.table`    = 'orders_index'
);

Partitioning strategies

The partition expression consists of a regular partition expression and a bucket partition expression, which together determine the number of partitions for the index data.

Regular partition expression

  • You can specify zero or more regular partition expressions, separated by commas.

  • The expression fields must come from the primary key fields of the wide table.

  • Index data is built based on partition values. Queries can leverage partition filtering to efficiently locate data.

  • We recommend that the data volume of a single partition be between 50 MB and 512 MB. Use caution when using high-cardinality fields (such as order IDs or user IDs) as regular partition keys, as this can produce a large number of small partitions and cause metadata bloat.

If the wide table does not have a natural date primary key like dt, but only has a timestamp primary key such as create_time, you can use a function in the regular partition expression to derive a date from the timestamp (for example, to partition by day). For more information, see Advanced usage — Complex partition expressions.

Bucket partition expression

  • At least one bucket partition expression must be specified.

  • Syntax: bucket(bucket_num, column_name): bucket_num is the number of buckets, and column_name is the bucket partition field.

  • Bucket number calculation: hash(column_name) % bucket_num. For example, bucket(128, id) means hash(id) % 128.

  • The bucket partition field must be a primary key field and should have sufficient dispersion to avoid data skew.

  • We recommend that the number of buckets be ≤ 1024.

View columnstore indexes

After a columnstore index is created, the index data is continuously built. Data synchronization includes full synchronization (backfilling historical data from the wide table) and incremental synchronization (consuming the wide table WAL for continuous appending). After entering the incremental phase, the synchronization latency is typically < 1 hour.

Use the SHOW INDEX statement to view all indexes (including secondary indexes and columnstore indexes) on a specified wide table:

SHOW INDEX FROM orders;

Sample output:

+-------------+------------+------------+-------------+---------------------------------+------------+---------------+---------------------------------+-----------+-------------------------------------------------------------+
| TABLE_SCHEMA| DATA_TABLE | INDEX_NAME | INDEX_STATE | INDEX_PROGRESS                  | INDEX_TYPE | INDEX_COVERED | INDEX_COLUMN                    | INDEX_TTL | INDEX_DESCRIPTION                                           |
+-------------+------------+------------+-------------+---------------------------------+------------+---------------+---------------------------------+-----------+-------------------------------------------------------------+
| default     | orders     | orders_idx | ACTIVE      | 2026-08-10 18:59:20.652 +0800   | COLUMNAR   | NA            | id,dt,user_id,amount,status     |           | index table: my_index_db.orders_index; partition by: [...]  |
+-------------+------------+------------+-------------+---------------------------------+------------+---------------+---------------------------------+-----------+-------------------------------------------------------------+

Field description

Field

Description

TABLE_SCHEMA

The name of the database where the wide table resides.

DATA_TABLE

The name of the wide table.

INDEX_NAME

The name of the index, which is the name specified when running CREATE INDEX.

INDEX_STATE

The index status. Common values for columnstore indexes:
BUILDING: The index is being built (full or incremental) and is not yet available for queries.
ACTIVE: The index is available for queries.

INDEX_PROGRESS

Incremental synchronization checkpoint.
• This field is empty during the BUILDING phase.
• After entering the ACTIVE state, it shows a timestamp (such as 2026-08-10 18:59:20.652 +0800), indicating that wide table data before this checkpoint has been fully synchronized to the columnstore index. The checkpoint advances continuously with incremental synchronization.

INDEX_TYPE

The index type. For columnstore indexes, the value is always COLUMNAR.

INDEX_COVERED

Whether the index is a covering index. For columnstore indexes, the value is always NA (not applicable).

INDEX_COLUMN

The list of wide table columns covered by the index.

INDEX_TTL

The index TTL. Columnstore indexes do not support TTL, so this field is always empty.

INDEX_DESCRIPTION

Diagnostic information for the index, in the format index table: .; partition by: [...]; .... The index table is the columnstore table database and table name that you specified in CREATE INDEX. Business queries directly access this table. The remaining fields are for internal diagnostics and can be provided to technical support for troubleshooting.

INDEX_COMMENT

Index comments, usually empty.

You can view the progress and real-time status of the Spark job for full build on the columnstore index task details page in the Lindorm console.

Use columnstore indexes

In a SELECT statement, use the hint _use_ldps_(cg_name), _columnar_index_ to route the query to the computing engine and hit the columnstore index.

Large-scale data aggregation

SELECT /*+ _use_ldps_(cg0), _columnar_index_ */
       dt, COUNT(*) AS order_cnt, SUM(amount) AS gmv, MAX(amount) AS max_amount
FROM my_index_db.orders_index
WHERE dt BETWEEN '2026-08-01' AND '2026-08-10'
GROUP BY dt;

Large-scale data sorting

SELECT /*+ _use_ldps_(cg0), _columnar_index_ */
       id, user_id, amount
FROM my_index_db.orders_index
WHERE dt = '2026-08-10' AND status = 'PAID'
ORDER BY amount DESC
LIMIT 100;

Large-scale data join

After creating columnstore indexes for multiple wide tables, you can perform JOIN operations across indexes:

SELECT /*+ _use_ldps_(cg0), _columnar_index_ */ *
FROM my_index_db.orders_index    AS o
JOIN my_index_db.users_index     AS u
  ON o.user_id = u.id
WHERE o.dt = '2026-08-10'
LIMIT 100;

Delete a columnstore index

DROP INDEX orders_idx ON orders;

For the complete syntax of DROP INDEX, see DROP INDEX.

You cannot create columnstore indexes with the same name on a wide table, regardless of whether the existing index is in a failed state. For a failed index, you must first run DROP INDEX to delete it before creating a new index.