Object table definition
MaxCompute introduces the object table feature, which enables the data warehouse compute engine to access unstructured data and its metadata in data lake storage. This topic describes the command syntax and provides usage examples.
Background
In many AI workflows, data warehouse developers who are familiar with the data and business logic must preprocess or process unstructured data for large models. These tasks leverage the low-cost, large-scale computing power of big data platforms. The processes and their results often interact with data in the data warehouse or data lake.
Using SQL to process unstructured data presents several challenges:
When reading from object storage, big data SQL engines cannot determine object sizes, which complicates execution plan optimization and makes it difficult to control concurrency or launch an appropriate number of concurrent tasks. Without effective predicate pushdown, the computing power cannot be fully utilized, especially in cases of data skew.
Reading metadata from object storage introduces high latency because each query requires a remote call to the storage service.
File lists from object storage can only be retrieved serially within a single process in a user-defined table function (UDTF), which results in poor data read performance.
You must implement authorization and network connectivity logic within user-defined functions (UDFs) to connect to storage services.
Traditional data warehouses do not provide features for securely uploading custom images or a safe execution environment for UDFs. Additionally, remote calls require complex concurrency management with the distributed computing service.
Features
MaxCompute introduces the object table feature, which enables the data warehouse compute engine to access unstructured data and its metadata in data lake storage. This feature provides the following capabilities:
Lets the engine read the metadata of OSS files as a table.
Leverages a metadata table to cache versioned metadata of OSS files. The SQL engine can then use this metadata for query optimizations, such as data filtering and predicate pushdown.
Provides built-in functions to read the content of unstructured data files in various ways.
Lets the MaxCompute SQL engine create concurrent splits based on object table metadata, unlocking large-scale distributed computing to improve data reading and processing efficiency.
Lets you upload a custom image to build UDFs that process unstructured data read by the engine.
Supports processing unstructured data to generate structured results and write them to internal or external tables in the data warehouse. Future versions will also support generating unstructured results and writing them back to OSS through object tables.
Supports the Maxframe engine from the Python ecosystem.
Limitations
The MaxCompute project must have schemas enabled. For more information, see Enable schema.
MaxCompute must support Type System 2.0.
Object tables do not currently support partitions.
Billing
An object table stores a collection of metadata for files in OSS. Storage fees are charged for the metadata that is refreshed and stored in the object table. For more information, see Storage pricing. Because the files from OSS are not stored within MaxCompute, MaxCompute does not charge for their storage. OSS charges for storing and accessing the data. For more information, see OSS storage pricing.
For tasks that extract and refresh OSS metadata, the
inputsizefor each scanned file is based on its metadata size, not its actual file size. Therefore, the total cost of the refresh job depends on the number of files, not their total size. For more information, see Pay-as-you-go billing for external tables.You incur computing fees when you use an object table and its metadata to analyze and extract unstructured data from OSS.
In a pay-as-you-go subscription, analyzing object table metadata is billed in the same way as an internal table. For more information, see Pay-as-you-go computing for standard tables. Processing the content of unstructured data from OSS is billed as an external table. For more information, see Pay-as-you-go billing for external tables.
In a subscription billing plan, you use prepaid subscription resources. For more information, see Computing fees (subscription).
Create an object table
Syntax
CREATE OBJECT TABLE [IF NOT EXISTS] <objecttable_name>
WITH SERDEPROPERTIES ('<key>' = '<value>')
LOCATION '<location>'
[TBLPROPERTIES ('<key>' = '<value>')]
[COMMENT '<comment>']
;Object tables must be used in a project that has schemas enabled, and you must turn on the schema syntax switch.
You do not need to define columns for an object table because its metadata columns are system-provided.
Parameters
Parameter | Required | Description |
objecttable_name | Yes | The name of the table. |
SERDEPROPERTIES ('<key>'='<value>') | Yes | Specifies properties for the serializer-deserializer (SerDe). You can specify a RAM role for authorization by setting the Example: Before you use this feature, ensure that you have completed one-click authorization for
Note One-click authorization is only possible if the MaxCompute project owner and the OSS account owner are the same. |
location | Yes |
|
TBLPROPERTIES ('<key>'='<value>') | No |
|
comment | No | A comment for the table. |
Example
SET odps.namespace.schema=true;
CREATE OBJECT TABLE ot_demo_day
WITH serdeproperties (
'odps.properties.rolearn'='acs:ram::xxxxxx:role/aliyunodpsdefaultrole')
LOCATION 'oss://oss-cn-hangzhou-internal.aliyuncs.com/odps-external-****/ottest/';View object table properties
Syntax
DESC <object_table_name>Parameters
object_table_name: Required. The name of the table.
Example
SET odps.namespace.schema=true;
DESC ot_demo_day; The following result is returned:
+------------------------------------------------------------------------------------+
| Owner: ALIYUN$****@test.aliyunid.com |
| Project: test_objecttable |
| Schema: default |
| TableComment: |
+------------------------------------------------------------------------------------+
| CreateTime: 2024-09-02 20:01:56 |
| LastDDLTime: 2024-09-02 20:01:56 |
| LastModifiedTime: 2024-09-02 20:01:56 |
+------------------------------------------------------------------------------------+
| InternalTable: YES | Size: 0 |
+------------------------------------------------------------------------------------+
| Native Columns: |
+------------------------------------------------------------------------------------+
| Field | Type | Label | Comment |
+------------------------------------------------------------------------------------+
| key | varchar(2048) | | The name of the object. |
| size | bigint | | The size of the returned object in bytes. |
| type | varchar(32) | | The type of the object and valid values: Normal, Multipart, Appendable, and Symlink. |
| last_modified | timestamp | | The last modified time of the object. |
| storage_class | varchar(32) | | The storage class of the object. |
| etag | varchar(64) | | The entity tag (ETag). When an object is created, an ETag is created to identify the content of the object. |
| restore_info | varchar(256) | | The restoration status of the object. |
| owner_id | bigint | | The ID of the bucket owner. |
| owner_display_name | varchar(256) | | The display name of the bucket owner. |
+------------------------------------------------------------------------------------+The following table describes key columns in the result.
Parameter | Type | Null allowed | Description |
key | VARCHAR(2048) The length constraint in OSS is 1,023 characters. For more information, see OSS object naming rules and examples. | False | The relative path of the object within the object table. |
size | BIGINT | False | The size of the object in bytes. |
type | VARCHAR(32) | False | The type of the object in OSS: Normal, Multipart, Appendable, or Symlink. |
last_modified | TIMESTAMP_NTZ | False | The time when the object's data was last modified in OSS. |
storage_class | VARCHAR(32) | False | The storage class of the object in OSS. For a list of storage classes, see Storage classes. |
etag | VARCHAR(64) | False | The ETag is an entity tag generated for an object. It identifies if an object's content has changed between updates, but it is not a unique identifier. |
restore_info | VARCHAR(256) | True | Indicates whether an object has been restored from cold storage. If an object is being restored, this column provides relevant information. |
owner_id | BIGINT | True | The ID of the object's owner. |
owner_display_name | VARCHAR(256) | True | The display name of the object's owner. |
View CREATE TABLE statement
Syntax
SHOW CREATE TABLE <object_table_name>;Parameters
object_table_name: Required. The name of the table.
Example
SET odps.namespace.schema=true;
SHOW CREATE TABLE ot_demo_day; The following result is returned:
CREATE OBJECT TABLE IF NOT EXISTS yunqi_object_****.`default`.ot_demo_day
WITH SERDEPROPERTIES (
'serialization.format'='1',
'odps.properties.rolearn'='acs:ram::139699392458****:role/aliyunodpsdefaultrole')
LOCATION
'oss://oss-cn-hangzhou-internal.aliyuncs.com/odps-external-****/ottest/'
TBLPROPERTIES (
'last_modified_time'='1731478307',
'transient_lastDdlTime'='1731478307',
'metadata.cache.mode'='manual',
'metadata.staleness.seconds'='3600');Refresh object table metadata
The actual data for an object table is stored in OSS. MaxCompute caches the metadata of these objects and uses this cached metadata for queries and computations. Therefore, you must refresh the cache before using an object table. You can refresh metadata manually or configure periodic refreshes when you create the table.
Both manual and periodic refreshes are full refreshes.
Manual refresh
Each refresh performs a full synchronization of the metadata. You can control the timing and frequency of these refreshes.
Syntax
ALTER TABLE <objecttable_name> REFRESH METADATA;Parameters
objecttable_name: Required. The name of the table.
Example
SET odps.namespace.schema=true; ALTER TABLE ot_demo_day REFRESH METADATA;
Periodic refresh
If the files in the OSS directory that the object table maps to change frequently, you can configure periodic metadata refreshes. This reduces maintenance costs by specifying the relevant parameters when you create the table.
Syntax
SET odps.namespace.schema=true; SET odps.sql.type.system.odps2 = true; CREATE OBJECT TABLE ot_demo_day WITH serdeproperties ( 'odps.properties.rolearn'='acs:ram::xxxxxx:role/aliyunodpsdefaultrole' ) location 'oss://oss-cn-hangzhou-internal.aliyuncs.com/odps-external-****/ottest/' tblproperties ( 'metadata.cache.mode' = 'periodic', 'metadata.staleness.seconds' = '3600' );Parameters
metadata.staleness.seconds: The refresh interval. This parameter is required for theperiodicmode. The value can range from[1, 604800], which represents a range from 1 second to 1 week. This parameter is a best-effort guarantee, and the scheduler will try to execute the refresh as close to the specified interval as possible.metadata.cache.mode: The refresh mode. The available options are:periodic: Periodic trigger.crontab: Scheduled refresh.manual: Manual trigger (default). You can control the trigger timing.
Scheduled refresh
If the files in the OSS directory that the object table maps to change frequently, you can schedule metadata refreshes. This reduces maintenance costs by specifying the relevant parameters when you create the table.
Syntax
SET odps.namespace.schema=true; SET odps.sql.type.system.odps2 = true; CREATE OBJECT TABLE ot_demo_day WITH SERDEPROPERTIES ( 'odps.properties.rolearn'='acs:ram::xxxxxx:role/aliyunodpsdefaultrole' ) LOCATION 'oss://oss-cn-region-internal.aliyuncs.com/odps-external-****/ottest/' TBLPROPERTIES ( 'metadata.cache.mode' = 'crontab', 'metadata.crontab.expression' = 'your_timed_expression' );Parameters
metadata.crontab.expression: A cron expression that defines the schedule. For example, to trigger a refresh at 2:00 PM every day, you can use the expression0 0 14 * * ?, which means0seconds,0minutes, hour14(2:00 PM),everyday,everymonth, and?to not specify a day of the week (mutually exclusive with the day-of-month field to avoid conflicts).metadata.cache.mode: The refresh mode. The available options are:crontab: Scheduled refresh.periodic: Periodic trigger.manual: Manual trigger (default). You can control the trigger timing.
View refresh tasks
You can run the following command to view the history of refresh tasks.
SHOW refresh task history FOR object TABLE <object_table_name>;Parameters
<object_table_name> must be an object table.
Return values: The instance ID of the refresh task (InstanceId), the creation time (CreateTime), the end time (EndTime), and the status (Status).
If the Status is Failed, you can run
wait InstanceId;to view the log and check for error details.
Example
-- View the historical refresh tasks for the object table. SET odps.namespace.schema=true; SHOW refresh task history for object table ot_demo_day04; -- The following result is returned. ID = 20260105*******f +---------------------------------------------------------------------------------------------------+ | Project: test_project | | Schema: default | | Task: *** | +---------------------------------------------------------------------------------------------------+ | History: | +---------------------------------------------------------------------------------------------------+ | InstanceId | CreateTime | EndTime | Status | +---------------------------------------------------------------------------------------------------+ | 20260105******************ks | 2026-01-05 14:12:00 | 2026-01-05 14:12:04 | Terminated | | 20260105******************y3 | 2026-01-05 14:10:00 | 2026-01-05 14:10:03 | Terminated | +---------------------------------------------------------------------------------------------------+ OK
Querying object tables
After an object table fetches the metadata of files from an OSS directory, you can query the table to browse this metadata. You can also use SQL statements to perform calculations on the metadata, such as filtering, matching, aggregations, joins, window functions, ORDER BY, and LIMIT.
Syntax
SELECT * FROM <object_table_name>;Parameters
object_table_name: Required. The name of the table.
Example
-- You can query the data uploaded to the specified OSS directory. If the amount of data is large, you can limit the result to five rows.
SET odps.namespace.schema=true;
SELECT * FROM ot_demo_day [limit 5];Query binary data
You can query the binary data of objects in an object table in one of the following two ways:
The _data column
The
_datacolumn returns the complete binary data of an object, such as an image's binary stream. Its data type is BINARY.To avoid out-of-memory errors, the binary data of a single object should not be too large. The following sample code shows how to use this column:
SET odps.namespace.schema=true; SELECT _data FROM test_ot1;The _blob column
The
_blobcolumn returns temporary credentials to download binary data, not the binary data itself. The_blobcolumn offers better computational stability and lower resource consumption than the_datacolumn. We recommend that you use the_datacolumn directly for smaller objects (less than 1 MB on average) and the_blobcolumn for larger objects. The usage is as follows:SET odps.namespace.schema=true; SELECT _blob FROM test_ot1;
Examples
_data column
Download the test file and upload it to OSS: ot_test.txt.
Create an object table.
SET odps.namespace.schema=true; SET odps.sql.type.system.odps2=true; SET odps.mcqa.disable=TRUE; DROP TABLE IF EXISTS test_ot1; CREATE OBJECT TABLE test_ot1 LOCATION 'oss://oss-cn-<region>-internal.aliyuncs.com/<bucket name>/<file path>/'; -- Refresh the table cache. ALTER TABLE test_ot1 REFRESH METADATA; SET odps.mcqa.disable=TRUE; SET odps.namespace.schema=true; SELECT * FROM test_ot1; -- The following result is returned: +------+------------+------+---------------+---------------+------+--------------+------------+--------------------+ | key | size | type | last_modified | storage_class | etag | restore_info | owner_id | owner_display_name | +------+------------+------+---------------+---------------+------+--------------+------------+--------------------+ | ot_test.txt | 286 |Normal|2026-07-21 06:49:59| Standard | 3*01 | NONE | 1***279014 | 1***279014 | +------+------------+------+---------------+---------------+------+--------------+------------+--------------------+Read the
_datacolumn.SET odps.namespace.schema=true; SELECT _data FROM test_ot1; -- The following result is returned: +-------+ | _data | +-------+ | {=0A=20=20"id":=20**1,=0A=20=20"title":=20"Hello=20World",=0A=20=20"description":=20"=E8=BF**E4=BE=8B=20JSON=20=**82",=0A=20=20"tags":=20[=0A*=20"=E7**=8B",=0A=20=20=20=20"JSON",=0A=20=20=20=20"Example"=0A=20=20],=0A=20=20"metadata":=20{=0A=20=20=20=20"author":=20"AI=20Assistant",=0A=20=20=20=20"created_at":=20"2023-10-01T12:00:00Z",=0A=20=20=20=20"is_active":=20true=0A=20=20}=0A} | +-------+View the text content.
SET odps.namespace.schema=true; SELECT CAST(_data AS STRING) FROM test_ot1; -- The following result is returned: +-----+ | _c0 | +-----+ | { "id": 1001, "title": "Hello World", "description": "This is sample JSON data that contains both Chinese and English.", "tags": [ "Programming", "JSON", "Example" ], "metadata": { "author": "AI Assistant", "created_at": "2023-10-01T12:00:00Z", "is_active": true } } | +-----+
_blob column
Download the test file and upload it to OSS: ot_test.txt.
Create an object table.
SET odps.namespace.schema=true; SET odps.sql.type.system.odps2=true; SET odps.mcqa.disable=TRUE; DROP TABLE IF EXISTS test_ot1; CREATE OBJECT TABLE test_ot1 LOCATION 'oss://oss-cn-<region>-internal.aliyuncs.com/<bucket name>/<file path>/'; -- Refresh the table cache. ALTER TABLE test_ot1 REFRESH METADATA; SET odps.mcqa.disable=TRUE; SET odps.namespace.schema=true; SELECT * FROM test_ot1; -- The following result is returned: +------+------------+------+---------------+---------------+------+--------------+------------+--------------------+ | key | size | type | last_modified | storage_class | etag | restore_info | owner_id | owner_display_name | +------+------------+------+---------------+---------------+------+--------------+------------+--------------------+ | ot_test.txt | 286 |Normal|2026-07-21 06:49:59| Standard | 3*01 | NONE | 1***279014 | 1***279014 | +------+------------+------+---------------+---------------+------+--------------+------------+--------------------+Read the
_blobcolumn.SET odps.namespace.schema=true; SELECT _blob FROM test_ot1; -- The following result is returned: +-------+ | _blob | +-------+ | CAEQAxruAgEAAAANA**AAEb2svMjAy3CIAKhR5WVBc/0S4GmxSm4psPJ5CV3k4STIeCgl***FyYTQ1ZXEx | +-------+View the text content.
SET odps.namespace.schema=true; SELECT CAST(read_blob(_blob) AS STRING) FROM test_ot1; -- The following result is returned: +-----+ | _c0 | +-----+ | { "id": 1001, "title": "Hello World", "description": "This is sample JSON data that contains both Chinese and English.", "tags": [ "Programming", "JSON", "Example" ], "metadata": { "author": "AI Assistant", "created_at": "2023-10-01T12:00:00Z", "is_active": true } } | +-----+
Query object content
By default, queries compute only on object metadata and do not read the object content. However, real-world computations often require the actual object content. MaxCompute provides a built-in download function to use in your computations.
GET_DATA_FROM_OSS syntax
The GET_DATA_FROM_OSS function reads all or part of an object's content and returns it as binary data.
BINARY GET_DATA_FROM_OSS (
STRING <full_object_table_name>,
STRING <key>
[, BIGINT <offset>]
[, BIGINT <length>]
[, STRING <object_not_found_policy>]
)Parameters
Parameter | Required | Data type | Description | Default |
| Yes | STRING | The full path to the OBJECT TABLE in the three-tier model, including the Project and Schema names, for example, | None |
| Yes | STRING | The object key (name) of the OSS object in the Object Table. For the exact value, see the | None |
| No | BIGINT | The byte position at which to start reading. Must be >= 0. |
|
| No | BIGINT | The number of bytes to read. |
|
| No | STRING | The behavior when an object key exists in the metadata cache but the object has been deleted from OSS. See the table below for valid values. |
|
Example 1
GET_DATA_FROM_OSS returns BINARY. To work with the content as text, wrap it in the STRING() function.
SET odps.namespace.schema=true;
SELECT STRING(
GET_DATA_FROM_OSS('<project_name>.default.ot_demo_day', key, 0, -1, 'OUTPUT_NULL')
)
FROM ot_demo_day;Example 2
The following statements all read the full content of the Object Table <project_name>.default.ot_demo_day. All six forms are equivalent — they each default offset to 0, length to -1, and object_not_found_policy to OUTPUT_NULL.
SET odps.namespace.schema=true;
-- Complete form with all parameters explicit
SELECT GET_DATA_FROM_OSS('<project_name>.default.ot_demo_day', key, 0, -1, 'OUTPUT_NULL') FROM ot_demo_day;
-- The following statements are equivalent to the one above
SELECT GET_DATA_FROM_OSS('<project_name>.default.ot_demo_day', key) FROM ot_demo_day;
SELECT GET_DATA_FROM_OSS('<project_name>.default.ot_demo_day', key, 0) FROM ot_demo_day;
SELECT GET_DATA_FROM_OSS('<project_name>.default.ot_demo_day', key, 0, -1) FROM ot_demo_day;
SELECT GET_DATA_FROM_OSS('<project_name>.default.ot_demo_day', key, 'OUTPUT_NULL') FROM ot_demo_day;
SELECT GET_DATA_FROM_OSS('<project_name>.default.ot_demo_day', key, 0, 'OUTPUT_NULL') FROM ot_demo_day;Query optimization
By default, when you access a table in a MaxCompute SQL query, the system creates uniform data splits based on the number of records, their size in bytes, and the total available computing resources (limited by your quota). This approach enables parallel processing of different splits, which effectively mitigates the long-tail problem and improves overall query performance.
However, this splitting method is not optimal for SQL queries that need to download object content from an object table for in-memory computation. In these cases, I/O operations can easily become a bottleneck, leading to a long-tail problem. Consider the following scenario:
Key | Size |
a0000.jpg | 10 MB |
a0001.jpg | 10 MB |
a0002.jpg | 10 MB |
... | ... |
a1022.jpg | 10 MB |
a1023.jpg | 10 MB |
b.avi | 10 GB |
Assume that only two workers are available. If splits are created based on the number of rows or record bytes, as with a standard internal table, the system might create two splits: split1 ([a0000.jpg ~ a0511.jpg]) and split2 ([a0512.jpg ~ a1023.jpg, b.avi]). The amount of data to download for split1 is 10 MB * 512 = 5 GB, while the amount for split2 is 5 GB + 10 GB = 15 GB. As a result, the computational load for split2 is significantly higher than for split1, causing a severe long-tail problem.
A more logical approach is to create splits based on the actual size of objects for queries that download them. This strategy minimizes the long-tail problem. If the logic for consuming OSS object data is even more time-consuming, a flexible splitting capability is crucial. For the scenario above, a better approach would be to create two splits: split1 with [a0000.jpg ~ a1023.jpg] and split2 with [b.avi]. In this case, the download size for both splits is 10 GB.
To support this, by default, MaxCompute object tables split tasks based on the actual size of the objects. The default split size is 1 GB, but you can adjust it to KB, MB, or GB levels as needed.
SET odps.namespace.schema=true;
-- The default is 1 GB, but you can adjust it.
SET odps.sql.object.table.split.unit.gb = 1;
SELECT get_data_from_oss('project.default.ot_demo_day', key) FROM ot_demo_da WHERE ...
-- Alternatively, you can control the split size at the MB level, which has higher priority than the GB parameter.
SET odps.sql.object.table.split.unit.mb = 1;
SELECT get_data_from_oss('project.default.ot_demo_day', key) FROM ot_demo_da WHERE ...
-- You can also control the split size at the KB level, which has the highest priority.
SET odps.sql.object.table.split.unit.kb = 1;
SELECT get_data_from_oss('project.default.ot_demo_day', key) FROM ot_demo_da WHERE ...In some cases, you may want to disable this query optimization. This optimization strategy launches two jobs, with the first job performing some preprocessing, which you can view in the Logview. MaxCompute provides an option to disable it:
SET odps.namespace.schema=true;
SET odps.sql.object.table.split.by.object.size.enabled = false;
SELECT get_data_from_oss('project.default.ot_demo_day', key) FROM ot_demo_day WHERE ...Delete an object table
An object table caches user metadata, which consumes storage and incurs costs. If you no longer need the cached data, you can delete the object table. You can recreate the object table later if needed.
Syntax
DROP TABLE [IF EXISTS] <object_table_name>; Parameters
object_table_name: Required. The name of the table.
Example
SET odps.namespace.schema=true;
DROP TABLE IF EXISTS ot_demo_day;FAQ
ODPS-0010000:System internal error
Symptom
The following error message is reported:
ODPS-0010000:System internal error - ActionHandler job failed with failinfo storage service worker error occured: common/io/oss/oss_file_system_cppsdk.cpp(919): OSSRequestException: Status: -50, RequestId: , ErrorCode: ClientError:-50, Message: E_HTTP_ERROR_CONN_REFUSEDCause
You used a public endpoint for OSS when creating the object table.
Solution
When you create an object table, the
locationparameter must contain an internal endpoint for theoss_endpoint. For information about how to obtain the internal endpoint, see Parameter description. If the error persists after you change the address to an internal endpoint, contact the MaxCompute technical support team by submitting a ticket for support.
Periodic refresh fails
Symptom
You set periodic refresh parameters when you created the object table. However, the refresh does not run when the interval is reached.
Solution
Ensure that the location parameter used to create the object table specifies an OSS internal endpoint. For more information about creating an object table, see Parameters.