Learn how to create, read, and write external tables for CSV and TSV data stored in Object Storage Service (OSS).
Usage notes
OSS external tables do not support the cluster property.
The size of a single file cannot exceed 2 GB. You must split files that are larger than 2 GB.
MaxCompute and OSS must be in the same region.
Supported data types
For more information about MaxCompute data types, see Data Type Version 1.0 and Data Type Version 2.0.
For more information about SmartParse, see Flexible type compatibility of Smart Parse.
Type | com.aliyun.odps.CsvStorageHandler/ TsvStorageHandler (Built-in) | org.apache.hadoop.hive.serde2.OpenCSVSerde (Open-source) |
TINYINT | ||
SMALLINT | ||
INT | ||
BIGINT | ||
BINARY | ||
FLOAT | ||
DOUBLE | ||
DECIMAL(precision,scale) | ||
VARCHAR(n) | ||
CHAR(n) | ||
STRING | ||
DATE | ||
DATETIME | ||
TIMESTAMP | ||
TIMESTAMP_NTZ | ||
BOOLEAN | ||
ARRAY | ||
MAP | ||
STRUCT | ||
JSON |
Supported compression formats
When you read from or write to compressed OSS files, you must include the with serdeproperties attribute in the CREATE TABLE statement. For more information, see with serdeproperties attribute parameters.
Compression format | com.aliyun.odps.CsvStorageHandler/ TsvStorageHandler (Built-in) | org.apache.hadoop.hive.serde2.OpenCSVSerde (Open-source) |
GZIP | ||
SNAPPY | ||
LZO | ||
ZSTD |
Supported schema evolution
Operation | Supported | Description |
Add column |
| |
Drop column | This operation is not recommended because it can cause a mismatch between the schema and the data. | |
Change column order | This operation is not recommended because it can cause a mismatch between the schema and the data. | |
Change column data type | For a list of supported data type conversions, see Change Column Data Type. | |
Rename column | ||
Modify column comment | The comment must be a valid string with a maximum length of 1,024 bytes. Otherwise, an error occurs. | |
Change column nullability | This operation is not supported. Columns are nullable by default. |
Parameter configuration
The schema of a CSV or TSV external table maps to file columns by position. If the number of columns in an OSS file does not match the number of columns in the external table's schema, you can use the odps.sql.text.schema.mismatch.mode parameter to specify how to handle mismatched rows.
If
odps.sql.text.schema.mismatch.modeis set to truncate, column modifications have the following effects:Data that conforms to the new schema can be read as expected.
Existing data that uses the old schema is read based on the new schema.
For example, if you add a column to a table, the historical data for that column appears as NULL when you read the table.
If
odps.sql.text.schema.mismatch.modeis set to ignore, column modifications have the following effects:Data that conforms to the new schema can be read as expected.
Existing data that uses the old schema is read based on the new schema.
For example, if you add a column to a table, entire rows of historical data that lack the new column are discarded when you read the table.
If
odps.sql.text.schema.mismatch.modeis set to error, column modifications have the following effects:Data that conforms to the new schema can be read as expected.
Existing data that uses the old schema is read based on the new schema.
For example, if you add a column to a table, an error occurs when you try to read historical data that lacks the new column.
Create an external table
Syntax
Built-in text parser
CSV format
CREATE EXTERNAL TABLE [IF NOT EXISTS] <mc_oss_extable_name>
(
<col_name> <data_type>,
...
)
[COMMENT <table_comment>]
[PARTITIONED BY (<col_name> <data_type>, ...)]
STORED BY 'com.aliyun.odps.CsvStorageHandler'
[WITH serdeproperties (
['<property_name>'='<property_value>',...]
)]
LOCATION '<oss_location>'
[tblproperties ('<tbproperty_name>'='<tbproperty_value>',...)];TSV format
CREATE EXTERNAL TABLE [IF NOT EXISTS] <mc_oss_extable_name>
(
<col_name> <data_type>,
...
)
[COMMENT <table_comment>]
[PARTITIONED BY (<col_name> <data_type>, ...)]
STORED BY 'com.aliyun.odps.TsvStorageHandler'
[WITH serdeproperties (
['<property_name>'='<property_value>',...]
)]
LOCATION '<oss_location>'
[tblproperties ('<tbproperty_name>'='<tbproperty_value>',...)];Built-in open-source parser
CREATE EXTERNAL TABLE [IF NOT EXISTS] <mc_oss_extable_name>
(
<col_name> <data_type>,
...
)
[COMMENT <table_comment>]
[PARTITIONED BY (<col_name> <data_type>, ...)]
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde'
[WITH serdeproperties (
['<property_name>'='<property_value>',...]
)]
STORED AS TEXTFILE
LOCATION '<oss_location>'
[tblproperties ('<tbproperty_name>'='<tbproperty_value>',...)];Common parameters
For more information about common parameters, see Basic syntax parameters.
Format-specific parameters
WITH SERDEPROPERTIES parameters
Applicable parser | Parameter | Use case | Description | Value | Default |
Built-in text data parser (CsvStorageHandler/TsvStorageHandler) | odps.text.option.gzip.input.enabled | Use this property to read CSV or TSV files that are compressed in GZIP format. | CSV and TSV compression property. Set this property to |
| False |
odps.text.option.gzip.output.enabled | Use this property to write data to OSS in GZIP compressed format. | CSV and TSV compression property. Set this property to |
| False | |
odps.text.option.header.lines.count | Use this property to skip the first N rows of a CSV or TSV file in OSS. | Specifies the number of header rows to skip from the beginning of the file when reading data. | Non-negative integer | 0 | |
odps.text.option.null.indicator | Use this property to define a custom string that represents a NULL value in the data. | MaxCompute parses the specified string as a For example, to interpret | string | empty string | |
odps.text.option.ignore.empty.lines | Use this property to define how to handle empty rows in a CSV or TSV file. | If |
| True | |
odps.text.option.encoding | Use this property when a data file does not use the default UTF-8 encoding. | The encoding specified here must match the file's actual encoding. A mismatch causes read failures. |
| UTF-8 | |
odps.text.option.delimiter | Use this property to specify the column delimiter for CSV or TSV files. | Ensure the specified delimiter correctly separates columns in your data file to prevent data misalignment. | Single character | Comma (,) | |
odps.text.option.use.quote | Use this property when a field in a CSV or TSV file contains line breaks (CRLF), double quotes, or the column delimiter. | When a field in a CSV file contains a newline, a double quote (you must add another |
| False | |
odps.sql.text.option.flush.header | Use this property to write a table header as the first row to each file block in OSS. | This property applies only to CSV files. |
| False | |
odps.sql.text.schema.mismatch.mode | Use this property when a row in the data file has a different number of columns than the external table's schema. | Specifies how to handle rows with a column count that mismatches the table schema. Note: This feature does not work if |
| error | |
odps.text.option.zstd.input.enabled | Use this property to read CSV or TSV files that are compressed in ZSTD format. | CSV and TSV compression property. Set this property to True for MaxCompute to read ZSTD-compressed files. Otherwise, the read operation fails. |
| False | |
odps.text.option.zstd.output.enabled | Use this property to write data to OSS in ZSTD compressed format. | CSV and TSV compression property. Set this property to True to compress data in ZSTD format when writing to OSS. Otherwise, the data is written uncompressed. |
| False | |
odps.text.option.snappy.input.enabled | Add this property when you need to read CSV or TSV files compressed with SNAPPY (SnappyRawCodec). | CSV and TSV compression property. MaxCompute can read compressed files only when this parameter is set to True. Otherwise, the read operation fails. |
| False | |
odps.text.option.snappy.output.enabled | Add this property when you need to write data to OSS compressed with SNAPPY (SnappyRawCodec). | CSV and TSV compression property. When this parameter is set to True, MaxCompute writes data to OSS in SNAPPY compression. Otherwise, data is written without compression. |
| False | |
Built-in open-source data parser (OpenCSVSerde) | separatorChar | Use this property to specify the column delimiter for CSV data stored as a TEXTFILE. | Specifies the column delimiter. | A single character | Comma (,) |
quoteChar | Use this property when fields in CSV data contain special characters such as the delimiter or line breaks. | Specifies the character used to quote fields. | A single character | None | |
escapeChar | Use this property to specify the escape character for CSV data stored as a TEXTFILE. | Specifies the character used to escape special characters within a field. | A single character | None |
tblproperties parameters
Applicable parser | Parameter | Use case | Description | Value | Default |
Built-in open-source data parser (OpenCSVSerde) | skip.header.line.count | Use this property to skip the first N rows of a CSV file stored as a TEXTFILE. | Specifies the number of header rows to skip from the beginning of the file when reading data. | Non-negative integer | None |
skip.footer.line.count | Use this property to skip the last N rows of a CSV file stored as a TEXTFILE. | Specifies the number of footer rows to skip from the end of the file when reading data. | Non-negative integer | None | |
mcfed.mapreduce.output.fileoutputformat.compress | Use this property to write TEXTFILE data to OSS with compression. | TEXTFILE compression property. If set to |
| False | |
mcfed.mapreduce.output.fileoutputformat.compress.codec | Use this property to specify the compression codec when writing compressed TEXTFILE data to OSS. When reading compressed CSV/TSV files whose filenames contain the .bz2, .deflate, .snappy, .gz, or .zstd suffix, no additional configuration is required. | TEXTFILE compression property. Sets the compression method for TEXTFILE data files. |
| None | |
odps.text.option.bad.row.skipping | Use this property to skip dirty data in CSV files stored in OSS. | Controls whether MaxCompute skips rows that are considered dirty data or reports an error. |
| None |
Whitelist and blacklist
MaxCompute OSS external tables support whitelist and blacklist filtering. By setting whitelist and blacklist parameters in tblproperties, you can filter which files to read from a directory. For details, see OSS external tables.
Write data
For details about the write syntax in MaxCompute, see Write syntax.
Query and analysis
See Query syntax for details on SELECT syntax.
See Query optimization for details on optimizing query plans.
See BadRowSkipping for details.
BadRowSkipping
The BadRowSkipping feature lets you skip bad rows in CSV data that would otherwise cause a query to fail. This setting controls error handling and does not affect how the underlying data format is parsed.
Parameters
Table-level parameter:
odps.text.option.bad.row.skippingrigid: Forces skipping. This setting cannot be overridden by session-level or project-level configurations.flexible: Enables skipping. This setting is flexible, allowing it to be overridden by session-level or project-level configurations.
session/projectlevel parametersThe
odps.sql.unstructured.text.bad.row.skippingparameter can override aflexibletable-level parameter, but not arigidone.on: Enables the feature. If the feature is not configured for the table, it is enabled by default.off: Disables the feature. If the table is configured as flexible, the feature is disabled. Otherwise, the setting of the table parameter is used.<null> or invalid input: The table-level configuration is used.
odps.sql.unstructured.text.bad.row.skipping.debug.num: Specifies the number of error results to print to stdout in Logview.The maximum value is 1000.
If the value is <=0, this feature is disabled.
If the value is invalid, this feature is disabled.
Interaction between session-level parameters and table properties
tbl property
session flag
result
rigid
on
On, Forced on
off
<null>, an invalid value, or the parameter is not configured
flexible
on
On
off
Off, Disabled by session
<null>, an invalid value, or the parameter is not configured
On
Not configured
on
On, Enabled by session
off
Off
<null>, an invalid value, or the parameter is not configured
Examples
Prepare the data
Upload the test data file csv_bad_row_skipping.csv, which contains bad rows, to a directory in OSS, such as
oss-mc-test/badrow/.Create the CSV external tables
The following examples show three scenarios based on different combinations of table-level and session-level parameters.
Table parameter:
odps.text.option.bad.row.skipping = flexible | rigid | <not set>Session flag:
odps.sql.unstructured.text.bad.row.skipping = on | off | <not set>
Parameter not set
-- No table-level parameter is set. Queries will fail on bad rows unless overridden by the session-level flag. CREATE EXTERNAL TABLE test_csv_bad_data_skipping_flag ( a INT, b INT ) STORED BY 'com.aliyun.odps.CsvStorageHandler' WITH serdeproperties ( 'odps.properties.rolearn'='acs:ram::<uid>:role/aliyunodpsdefaultrole' ) location '<oss://<your-bucket-name>/<your-file-path>/>';Flexible skipping
-- The table is configured to skip bad rows, but this can be disabled by the session-level flag. CREATE EXTERNAL TABLE test_csv_bad_data_skipping_flexible ( a INT, b INT ) STORED BY 'com.aliyun.odps.CsvStorageHandler' WITH serdeproperties ( 'odps.properties.rolearn'='acs:ram::<uid>:role/aliyunodpsdefaultrole' ) location '<oss://<your-bucket-name>/<your-file-path>/>' tblproperties ( 'odps.text.option.bad.row.skipping' = 'flexible' -- Enables flexible skipping, which can be disabled at the session level. );Rigid skipping
-- The table is configured to forcibly skip bad rows. This cannot be disabled at the session level. CREATE EXTERNAL TABLE test_csv_bad_data_skipping_rigid ( a INT, b INT ) STORED BY 'com.aliyun.odps.CsvStorageHandler' WITH serdeproperties ( 'odps.properties.rolearn'='acs:ram::<uid>:role/aliyunodpsdefaultrole' ) location '<oss://<your-bucket-name>/<your-file-path>/>' tblproperties ( 'odps.text.option.bad.row.skipping' = 'rigid' -- Forces skipping on. );Verify query results
Parameter not set
-- The following command enables skipping, but it is immediately overridden by the next command in this example. SET odps.sql.unstructured.text.bad.row.skipping=on; -- This command disables skipping and is the active setting for the SELECT query below, causing it to fail. SET odps.sql.unstructured.text.bad.row.skipping=off; -- You can use this command to print details of skipped rows when skipping is enabled. It has no effect here because the query fails. SET odps.sql.unstructured.text.bad.row.skipping.debug.num=10; SELECT * FROM test_csv_bad_data_skipping_flag;The query fails with the following error: FAILED: ODPS-0123131:User defined function exception
Flexible skipping
-- The following command enables skipping, but it is immediately overridden by the next command in this example. SET odps.sql.unstructured.text.bad.row.skipping=on; -- This command disables skipping, overriding the table's 'flexible' setting. It is the active setting for the SELECT query below, causing it to fail. SET odps.sql.unstructured.text.bad.row.skipping=off; -- Print details of up to 10 bad rows at the session level. The maximum is 1,000. A value of 0 or less disables printing. SET odps.sql.unstructured.text.bad.row.skipping.debug.num=10; SELECT * FROM test_csv_bad_data_skipping_flexible;The query fails with the following error: FAILED: ODPS-0123131:User defined function exception
Rigid skipping
-- This command is redundant because the 'rigid' setting already enforces skipping. SET odps.sql.unstructured.text.bad.row.skipping=on; -- This command attempts to disable skipping, but it is ignored because the 'rigid' table setting cannot be overridden. SET odps.sql.unstructured.text.bad.row.skipping=off; -- This command prints details for up to 10 bad rows that are skipped by the 'rigid' setting. SET odps.sql.unstructured.text.bad.row.skipping.debug.num=10; SELECT * FROM test_csv_bad_data_skipping_rigid;The following result is returned:
+------------+------------+ | a | b | +------------+------------+ | 1 | 26 | | 5 | 37 | +------------+------------+
Flexible type compatibility with Smart Parse
For CSV-formatted external tables in OSS, MaxCompute SQL uses data type 2.0 for read and write operations. Previously, only values in strict formats were supported. This feature provides flexible type compatibility for reading a wide range of value formats from CSV files. The specific parsing rules are detailed below.
Type | Input as string | Output as string | Description |
BOOLEAN |
Note A |
| Parsing fails if the input string is not one of the supported values. |
TINYINT |
Note
|
| 8-bit integer. An error occurs if the value is outside the range |
SMALLINT | 16-bit integer. An error occurs if the value is outside the range | ||
INT | 32-bit integer. An error occurs if the value is outside the range | ||
BIGINT | 64-bit integer. An error occurs if the value is outside the range Note The value | ||
FLOAT |
Note
|
| Special values (case-insensitive) include NaN, Inf, -Inf, Infinity, and -Infinity. An error occurs if a value is out of range. If the precision exceeds the limit, the value is rounded off. |
DOUBLE |
Note
|
| Special values (case-insensitive) include NaN, Inf, -Inf, Infinity, and -Infinity. An error occurs if a value is out of range. If the precision exceeds the limit, the value is rounded off. |
DECIMAL (precision, scale) Example: DECIMAL(15,2) |
Note
|
| An error occurs if the integer part contains more than An error is reported. If the fractional part exceeds the scale, the value is rounded off and truncated. |
CHAR(n) Example: CHAR(7) |
|
| The maximum length is 255. If an input string is shorter than n, it is padded with trailing spaces, but these spaces are ignored in comparisons. If an input string is longer than n, it is truncated. |
VARCHAR(n) Example: VARCHAR(7) |
|
| The maximum length is 65,535. If an input string is longer than n, it is truncated. |
STRING |
|
| The maximum length is 8 MB. |
DATE |
Note You can also set the |
|
|
TIMESTAMP_NTZ Note OpenCsvSerde does not support this type because it is incompatible with the Hive data format. |
|
|
|
DATETIME |
| Assuming the system time zone is Asia/Shanghai:
|
|
TIMESTAMP |
| (Assuming the system time zone is Asia/Shanghai)
|
|
General rules
For any data type, an empty string in the CSV data file is parsed as NULL when read into a table.
Unsupported data types
Complex types (STRUCT, ARRAY, MAP): Not supported. Values of these types often contain characters such as the comma (
,), which can conflict with common CSV delimiters and cause parsing failures.BINARY and INTERVAL: Not currently supported. If you need support for these types, contact MaxCompute technical support.
Numeric types (INT, DOUBLE, etc.)
For numeric data types such as INT, SMALLINT, TINYINT, BIGINT, FLOAT, DOUBLE, and DECIMAL, MaxCompute provides extensive default parsing capabilities.
If you need to parse only basic numeric strings, you can set the
odps.text.option.smart.parse.levelproperty tonaiveintblproperties. In naive mode, the parser supports only simple formats like "123" and "123.456". Parsing other string formats causes an error.
Date and time types (DATE, TIMESTAMP, etc.)
The
java.time.format.DateTimeFormatterclass processes all four date and time types:DATE, DATETIME, TIMESTAMP, and TIMESTAMP_NTZ.Default formats: MaxCompute has several built-in parsing formats.
Custom formats:
You can define multiple parsing formats and one output format by setting the
odps.text.option.<date|datetime|timestamp|timestamp_ntz>.io.formatproperty intblproperties.Use the hash symbol (
#) to separate multiple parsing patterns.Custom formats take precedence over the built-in formats. The first custom pattern is used for output.
Example: If you define the custom format string for the DATE type as
pattern1#pattern2#pattern3, MaxCompute can parse strings that matchpattern1,pattern2, orpattern3. However, when writing data to a file, the output will always use the format specified bypattern1. For more information, see DateTimeFormatter.
Important note on the 'z' time zone pattern
Avoid using 'z' (time-zone name) in custom formats, especially for users in China, because it is ambiguous.
Instead, use 'x' (zone-offset) or 'VV' (time-zone ID) for the time zone pattern.
Example: 'CST' typically means China Standard Time (UTC+8) in China. However, when
java.time.format.DateTimeFormatterparses 'CST', it interprets it as US Central Standard Time (UTC-6), which can cause unexpected input or output results.
CSV file splitting logic
The built-in CSV/TSV parser (OpenCSVSerde)
The built-in CSV/TSV parser (OpenCSVSerde) requires each line of data in a CSV file to be separated by \r\n or similar characters, and columns must not contain \r\n The parallel splitting and data integrity logic is as follows:
first, the file is split by split size, and some lines may be cut in the middle.
When subsequent workers consume splits, all splits except the first one actively skip the leading partial or full line.
Each worker must also actively consume the trailing partial or full line, even if it falls within the next split's range.
This parser supports parallel splitting but does not support quote escaping.
The open-source CSV parser (CsvStorageHandler / TsvStorageHandler)
The open-source CSV parser (CsvStorageHandler / TsvStorageHandler) takes quote escaping into account when reading CSV/TSV files and supports \r\n. It can handle scenarios where special characters such as nested quotes or \r\n appear inside quotes.
For example, in the data "a\ra","b\nb","cc""cc", the \r\n and double quotes can be correctly parsed and output, supporting cross-line values. However, because the actual line break positions can only be determined through data parsing, the file cannot be simply split by \r\n. As a result, parallel consumption of a single file is not supported.
If you confirm that the data columns do not contain \r\n and \r\n is used only as a line delimiter, you can enable parallel splitting by setting odps.sql.unstructured.data.single.file.split.enabled. In this case, a large file can be divided into multiple splits by split size, and the built-in Text Extractor automatically aligns to line break boundaries to ensure data integrity.
Conclusion:
If the data contains \r\n that cannot be treated as a line delimiter, only the built-in CSV/TSV parser can be used.
Conversely, if you need to split a single large file in parallel, only the open-source CSV Serde parser can be used.
Examples
Prerequisites
A MaxCompute project. See Create a MaxCompute project.
An OSS bucket in the same region as your MaxCompute project. See Create a bucket and Manage folders. MaxCompute can create OSS folders automatically — you do not need to create them before running SQL.
OSS access permissions via an Alibaba Cloud account, a Resource Access Management (RAM) user, or a RAM role. See Authorize access in STS mode for OSS.
The
CreateTablepermission in your MaxCompute project. See MaxCompute permissions.
Create an OSS external table with the built-in text parser
Example 1: Non-partitioned table
Map the external table to the
Demo1/directory from the sample data. Use the following command to create an OSS external table.CREATE EXTERNAL TABLE IF NOT EXISTS mc_oss_csv_external1 ( vehicleId INT, recordId INT, patientId INT, calls INT, locationLatitute DOUBLE, locationLongtitue DOUBLE, recordTime STRING, direction STRING ) STORED BY 'com.aliyun.odps.CsvStorageHandler' WITH serdeproperties ( 'odps.properties.rolearn'='acs:ram::<uid>:role/aliyunodpsdefaultrole' ) LOCATION 'oss://oss-cn-hangzhou-internal.aliyuncs.com/oss-mc-test/Demo1/'; -- You can run the `desc extended mc_oss_csv_external1;` command to view the schema of the created OSS external table.This example uses the
aliyunodpsdefaultroleRAM role. If you use a different RAM role, replacealiyunodpsdefaultrolewith the name of your target RAM role and grant it the required permissions to access OSS.Query the non-partitioned external table.
SELECT * FROM mc_oss_csv_external1;The command returns the following result:
+------------+------------+------------+------------+------------------+-------------------+----------------+------------+ | vehicleid | recordid | patientid | calls | locationlatitute | locationlongtitue | recordtime | direction | +------------+------------+------------+------------+------------------+-------------------+----------------+------------+ | 1 | 1 | 51 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | S | | 1 | 2 | 13 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | NE | | 1 | 3 | 48 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | NE | | 1 | 4 | 30 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | W | | 1 | 5 | 47 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | S | | 1 | 6 | 9 | 1 | 46.81006 | -92.08174 | 9/15/2014 0:00 | S | | 1 | 7 | 53 | 1 | 46.81006 | -92.08174 | 9/15/2014 0:00 | N | | 1 | 8 | 63 | 1 | 46.81006 | -92.08174 | 9/15/2014 0:00 | SW | | 1 | 9 | 4 | 1 | 46.81006 | -92.08174 | 9/15/2014 0:00 | NE | | 1 | 10 | 31 | 1 | 46.81006 | -92.08174 | 9/15/2014 0:00 | N | +------------+------------+------------+------------+------------------+-------------------+------------+----------------+Write data to the non-partitioned external table and check that the data was written successfully.
INSERT INTO mc_oss_csv_external1 VALUES(1,12,76,1,46.81006,-92.08174,'9/14/2014 0:10','SW'); SELECT * FROM mc_oss_csv_external1 WHERE recordId=12;The command returns the following result:
+------------+------------+------------+------------+------------------+-------------------+----------------+------------+ | vehicleid | recordid | patientid | calls | locationlatitute | locationlongtitue | recordtime | direction | +------------+------------+------------+------------+------------------+-------------------+----------------+------------+ | 1 | 12 | 76 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:10 | SW | +------------+------------+------------+------------+------------------+-------------------+----------------+------------+Verify that a new file appears in the
Demo1/directory in OSS.After the data is written, you can view the generated result file
20250606054845430gpwnhakujm16_M1_1_0_0-0_TableSink1-0-.csv(0.046 KB) in the corresponding OSS path, along with the original data filevehicle.csv(0.45 KB).
Example 2: Partitioned table
Map the external table to the
Demo2/directory from the sample data. The following sample command creates a partitioned OSS external table.CREATE EXTERNAL TABLE IF NOT EXISTS mc_oss_csv_external2 ( vehicleId INT, recordId INT, patientId INT, calls INT, locationLatitute DOUBLE, locationLongtitue DOUBLE, recordTime STRING ) PARTITIONED BY ( direction STRING ) STORED BY 'com.aliyun.odps.CsvStorageHandler' WITH serdeproperties ( 'odps.properties.rolearn'='acs:ram::<uid>:role/aliyunodpsdefaultrole' ) LOCATION 'oss://oss-cn-hangzhou-internal.aliyuncs.com/oss-mc-test/Demo2/'; -- You can run the `DESC EXTENDED mc_oss_csv_external2;` command to view the schema of the created external table.This example uses the
aliyunodpsdefaultroleRAM role. If you use a different RAM role, replacealiyunodpsdefaultrolewith the name of your target RAM role and grant it the required permissions to access OSS.Import partition data. If you create a partitioned OSS external table, you must also import the partition data. For more information, see OSS external tables.
MSCK REPAIR TABLE mc_oss_csv_external2 ADD PARTITIONS; -- This is equivalent to the following statement. ALTER TABLE mc_oss_csv_external2 ADD PARTITION (direction = 'N') PARTITION (direction = 'NE') PARTITION (direction = 'S') PARTITION (direction = 'SW') PARTITION (direction = 'W');Query the partitioned external table.
SELECT * FROM mc_oss_csv_external2 WHERE direction='NE';The command returns the following result:
+------------+------------+------------+------------+------------------+-------------------+----------------+------------+ | vehicleid | recordid | patientid | calls | locationlatitute | locationlongtitue | recordtime | direction | +------------+------------+------------+------------+------------------+-------------------+----------------+------------+ | 1 | 2 | 13 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | NE | | 1 | 3 | 48 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | NE | | 1 | 9 | 4 | 1 | 46.81006 | -92.08174 | 9/15/2014 0:00 | NE | +------------+------------+------------+------------+------------------+-------------------+----------------+------------+Write data to the partitioned external table and check that the data was written successfully.
INSERT INTO mc_oss_csv_external2 PARTITION(direction='NE') VALUES(1,12,76,1,46.81006,-92.08174,'9/14/2014 0:10'); SELECT * FROM mc_oss_csv_external2 WHERE direction='NE' AND recordId=12;The command returns the following result:
+------------+------------+------------+------------+------------------+-------------------+----------------+------------+ | vehicleid | recordid | patientid | calls | locationlatitute | locationlongtitue | recordtime | direction | +------------+------------+------------+------------+------------------+-------------------+----------------+------------+ | 1 | 12 | 76 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:10 | NE | +------------+------------+------------+------------+------------------+-------------------+----------------+------------+Check that a new file is generated in the
Demo2/direction=NEdirectory in OSS.The automatically generated partition data file
20250606062610590gocsdsoujm16_M1_1_0_0-0_TableSink1-0-.csvcan be seen in the OSS file list, indicating that data has been successfully written to the corresponding partition path in OSS.
Example 3: Compressed data
This example shows how to create a GZIP-compressed CSV external table and perform read and write operations.
Create an internal table and insert test data for a subsequent write test.
CREATE TABLE vehicle_test( vehicleid INT, recordid INT, patientid INT, calls INT, locationlatitute DOUBLE, locationlongtitue DOUBLE, recordtime STRING, direction STRING ); INSERT INTO vehicle_test VALUES (1,1,51,1,46.81006,-92.08174,'9/14/2014 0:00','S');Create a GZIP-compressed CSV external table and map it to the
Demo3/directory (which contains compressed data) from the sample data. The following sample command creates the OSS external table.CREATE EXTERNAL TABLE IF NOT EXISTS mc_oss_csv_external3 ( vehicleId INT, recordId INT, patientId INT, calls INT, locationLatitute DOUBLE, locationLongtitue DOUBLE, recordTime STRING, direction STRING ) PARTITIONED BY (dt STRING) STORED BY 'com.aliyun.odps.CsvStorageHandler' WITH serdeproperties ( 'odps.properties.rolearn'='acs:ram::<uid>:role/aliyunodpsdefaultrole', 'odps.text.option.gzip.input.enabled'='true', 'odps.text.option.gzip.output.enabled'='true' ) LOCATION 'oss://oss-cn-hangzhou-internal.aliyuncs.com/oss-mc-test/Demo3/'; -- Import partition data. MSCK REPAIR TABLE mc_oss_csv_external3 ADD PARTITIONS; -- You can run the `DESC EXTENDED mc_oss_csv_external3;` command to view the schema of the created external table.This example uses the
aliyunodpsdefaultroleRAM role. If you use a different RAM role, replacealiyunodpsdefaultrolewith the name of your target RAM role and grant it the required permissions to access OSS.Use a MaxCompute client to read data from OSS:
NoteIf the compressed data in OSS is in an open-source data format, you must add the
set odps.sql.hive.compatible=true;command before the SQL statement and submit them together for execution.--Enable a full table scan for the current session only. SET odps.sql.allow.fullscan=true; SELECT recordId, patientId, direction FROM mc_oss_csv_external3 WHERE patientId > 25;The command returns the following result:
+------------+------------+------------+ | recordid | patientid | direction | +------------+------------+------------+ | 1 | 51 | S | | 3 | 48 | NE | | 4 | 30 | W | | 5 | 47 | S | | 7 | 53 | N | | 8 | 63 | SW | | 10 | 31 | N | +------------+------------+------------+Read data from the internal table and write it to the OSS external table.
You can execute the
INSERT OVERWRITEorINSERT INTOcommand on an external table from the MaxCompute client to write data to OSS.INSERT INTO TABLE mc_oss_csv_external3 PARTITION (dt='20250418') SELECT * FROM vehicle_test;After the command runs successfully, you can view the exported file in the OSS directory.
Create an external table with a header row
Create a Demo11 directory in the oss-mc-test bucket from the sample data, and run the following statements:
--Create the external table.
CREATE EXTERNAL TABLE mf_oss_wtt
(
id BIGINT,
name STRING,
tran_amt DOUBLE
)
STORED BY 'com.aliyun.odps.CsvStorageHandler'
WITH serdeproperties (
'odps.text.option.header.lines.count' = '1',
'odps.sql.text.option.flush.header' = 'true'
)
LOCATION 'oss://oss-cn-hangzhou-internal.aliyuncs.com/oss-mc-test/Demo11/';
--Insert data.
INSERT OVERWRITE TABLE mf_oss_wtt VALUES (1, 'val1', 1.1),(2, 'value2', 1.3);
--Query data.
--When you create the table, you can define all columns as STRING. Otherwise, an error occurs when the header is read.
--Alternatively, add the 'odps.text.option.header.lines.count' = '1' parameter to the table definition to skip the header.
SELECT * FROM mf_oss_wtt;This example uses the aliyunodpsdefaultrole RAM role. If you use a different RAM role, replace aliyunodpsdefaultrole with the name of your target RAM role and grant it the required permissions to access OSS.
The command returns the following result:
+----------+--------+------------+
| id | name | tran_amt |
+----------+--------+------------+
| 1 | val1 | 1.1 |
| 2 | value2 | 1.3 |
+----------+--------+------------+Create an external table with mismatched columns
Create a
demodirectory in theoss-mc-testbucket from the sample data and upload thetest.csvfile. Thetest.csvfile contains the following content:1,kyle1,this is desc1 2,kyle2,this is desc2,this is two 3,kyle3,this is desc3,this is three, I have 4 columnsCreate the external tables.
Set the handling method for rows with inconsistent column counts to
TRUNCATE.-- Drop the table. DROP TABLE test_mismatch; -- Create the external table. CREATE EXTERNAL TABLE IF NOT EXISTS test_mismatch ( id string, name string, dect string, col4 string ) STORED BY 'com.aliyun.odps.CsvStorageHandler' WITH serdeproperties ('odps.sql.text.schema.mismatch.mode' = 'truncate') LOCATION 'oss://oss-cn-hangzhou-internal.aliyuncs.com/oss-mc-test/demo/';Specify the handling method for rows with inconsistent column counts as
IGNORE.-- Drop the table. DROP TABLE test_mismatch01; -- Create the external table. CREATE EXTERNAL TABLE IF NOT EXISTS test_mismatch01 ( id STRING, name STRING, dect STRING, col4 STRING ) STORED BY 'com.aliyun.odps.CsvStorageHandler' WITH serdeproperties ('odps.sql.text.schema.mismatch.mode' = 'ignore') LOCATION 'oss://oss-cn-hangzhou-internal.aliyuncs.com/oss-mc-test/demo/';Query the data in the tables.
Query the
test_mismatchtable.SELECT * FROM test_mismatch; --Returned result +----+-------+---------------+---------------+ | id | name | dect | col4 | +----+-------+---------------+---------------+ | 1 | kyle1 | this is desc1 | NULL | | 2 | kyle2 | this is desc2 | this is two | | 3 | kyle3 | this is desc3 | this is three | +----+-------+---------------+---------------+Query the
test_mismatch01table.SELECT * FROM test_mismatch01; --Returned result +----+-------+----------------+-------------+ | id | name | dect | col4 | +----+-------+----------------+-------------+ | 2 | kyle2 | this is desc2 | this is two +----+-------+----------------+-------------+
Create an external table with the open-source parser
This example shows how to use the built-in open source parser to create an OSS external table to read a comma-separated file while ignoring the header and footer rows.
Create a
demo-testdirectory in theoss-mc-testbucket from the sample data and upload the test.csv file.The test file contains the following data:
1,1,51,1,46.81006,-92.08174,9/14/2014 0:00,S 1,2,13,1,46.81006,-92.08174,9/14/2014 0:00,NE 1,3,48,1,46.81006,-92.08174,9/14/2014 0:00,NE 1,4,30,1,46.81006,-92.08174,9/14/2014 0:00,W 1,5,47,1,46.81006,-92.08174,9/14/2014 0:00,S 1,6,9,1,46.81006,-92.08174,9/15/2014 0:00,S 1,7,53,1,46.81006,-92.08174,9/15/2014 0:00,N 1,8,63,1,46.81006,-92.08174,9/15/2014 0:00,SW 1,9,4,1,46.81006,-92.08174,9/15/2014 0:00,NE 1,10,31,1,46.81006,-92.08174,9/15/2014 0:00,NCreate the external table, specify the comma as the separator, and set parameters to ignore the header and footer rows.
CREATE EXTERNAL TABLE ext_csv_test08 ( vehicleId INT, recordId INT, patientId INT, calls INT, locationLatitute DOUBLE, locationLongtitue DOUBLE, recordTime STRING, direction STRING ) ROW FORMAT serde 'org.apache.hadoop.hive.serde2.OpenCSVSerde' WITH serdeproperties ( "separatorChar" = "," ) stored AS textfile location 'oss://oss-cn-hangzhou-internal.aliyuncs.com/***/' -- Set parameters to ignore the header and footer rows. TBLPROPERTIES ( "skip.header.line.COUNT"="1", "skip.footer.line.COUNT"="1" ) ;Read data from the external table.
SELECT * FROM ext_csv_test08; -- The result includes 8 rows of data because the header and footer rows are ignored. +------------+------------+------------+------------+------------------+-------------------+----------------+------------+ | vehicleid | recordid | patientid | calls | locationlatitute | locationlongtitue | recordtime | direction | +------------+------------+------------+------------+------------------+-------------------+----------------+------------+ | 1 | 2 | 13 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | NE | | 1 | 3 | 48 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | NE | | 1 | 4 | 30 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | W | | 1 | 5 | 47 | 1 | 46.81006 | -92.08174 | 9/14/2014 0:00 | S | | 1 | 6 | 9 | 1 | 46.81006 | -92.08174 | 9/15/2014 0:00 | S | | 1 | 7 | 53 | 1 | 46.81006 | -92.08174 | 9/15/2014 0:00 | N | | 1 | 8 | 63 | 1 | 46.81006 | -92.08174 | 9/15/2014 0:00 | SW | | 1 | 9 | 4 | 1 | 46.81006 | -92.08174 | 9/15/2014 0:00 | NE | +------------+------------+------------+------------+------------------+-------------------+----------------+------------+
Create a CSV external table with custom time types
For details about parsing and output formats for custom time types in CSV, see Flexible type compatibility with Smart Parse.
Create a CSV external table that uses various time data types, such as
DATE,DATETIME,TIMESTAMP, andTIMESTAMP_NTZ.CREATE EXTERNAL TABLE test_csv ( col_date DATE, col_datetime DATETIME, col_timestamp TIMESTAMP, col_timestamp_ntz TIMESTAMP_NTZ ) STORED BY 'com.aliyun.odps.CsvStorageHandler' LOCATION 'oss://oss-cn-hangzhou-internal.aliyuncs.com/oss-mc-test/demo/' TBLPROPERTIES ( 'odps.text.option.date.io.format' = 'MM/dd/yyyy', 'odps.text.option.datetime.io.format' = 'yyyy-MM-dd-HH-mm-ss x', 'odps.text.option.timestamp.io.format' = 'yyyy-MM-dd HH-mm-ss VV', 'odps.text.option.timestamp_ntz.io.format' = 'yyyy-MM-dd HH:mm:ss.SS' ); INSERT OVERWRITE test_csv VALUES(DATE'2025-02-21', DATETIME'2025-02-21 08:30:00', TIMESTAMP'2025-02-21 12:30:00', TIMESTAMP_NTZ'2025-02-21 16:30:00.123456789');After the data is inserted, the content of the CSV file is as follows:
02/21/2025,2025-02-21-08-30-00 +08,2025-02-21 12-30-00 Asia/Shanghai,2025-02-21 16:30:00.12Query the data again to view the result.
SELECT * FROM test_csv;The command returns the following result:
+------------+---------------------+---------------------+------------------------+ | col_date | col_datetime | col_timestamp | col_timestamp_ntz | +------------+---------------------+---------------------+------------------------+ | 2025-02-21 | 2025-02-21 08:30:00 | 2025-02-21 12:30:00 | 2025-02-21 16:30:00.12 | +------------+---------------------+---------------------+------------------------+
FAQ
Column count mismatch error
Symptom
This error occurs when the column count in a row of a CSV or TSV file does not match the one defined in the external table DDL. MaxCompute reports an error similar to
FAILED: ODPS-0123131:User defined function exception - Traceback:java.lang.RuntimeException: SCHEMA MISMATCH:xxx.Resolution
You can control how MaxCompute handles the mismatch by setting the
odps.sql.text.schema.mismatch.modeparameter at the session level:SET odps.sql.text.schema.mismatch.mode=error: Fails the query when a column count mismatch occurs. This is the default behavior.SET odps.sql.text.schema.mismatch.mode=truncate: If a row has more columns than defined in the external table DDL, the extra columns are discarded. If a row has fewer columns, the missing columns are filled with NULL.