CSV/TSV外部表

更新时间:
复制为 MD 格式

本文介绍CSV、TSV格式的OSS外部表的创建、读取及写入方法。

适用范围

  • OSS外部表不支持cluster属性。

  • 单个文件大小不能超过2GB,如果文件过大,建议拆分。

  • MaxCompute需要与OSS部署在同一地域。

支持数据类型

MaxCompute数据类型详情请参见1.0数据类型版本2.0数据类型版本

更多有关SmartParse的说明,请参见Smart Parse更灵活的类型兼容能力

数据类型

com.aliyun.odps.CsvStorageHandler/

TsvStorageHandler

(内置文本数据解析器)

org.apache.hadoop.hive.serde2.OpenCSVSerde

(内置开源数据解析器)

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

不支持

不支持

支持压缩格式

当读写压缩属性的OSS文件时,需要在建表语句中添加with serdeproperties属性配置,详情请参见with serdeproperties属性参数

压缩格式

com.aliyun.odps.CsvStorageHandler/

TsvStorageHandler

(内置文本数据解析器)

org.apache.hadoop.hive.serde2.OpenCSVSerde

(内置开源数据解析器)

GZIP

支持

支持

SNAPPY

不支持

支持

LZO

不支持

支持

支持Schema Evolution

操作类型

是否支持

说明

添加列

支持

  • 不支持添加设置默认值的列。

  • 不支持添加复杂类型列、嵌套列。

删除列

支持

不建议使用,会出现Schema和数据不对应的问题。

修改列顺序

支持

不建议使用,会出现Schema和数据不对应的问题。

更改列数据类型

支持

数据类型支持转换表见更改列数据类型

修改列名

支持

修改列注释

支持

注释内容为长度不超过1024字节的有效字符串,否则报错。

修改列的非空属性

不支持

不支持此操作,默认为Nullable。

参数配置

CSVTSV外表的Schema和文件列之间按位置映射。当读取的OSS文件的文件列数和外部表的Schema列不一致时,可通过odps.sql.text.schema.mismatch.mode参数指定对于列数不一致行的处理方式。

  • odps.sql.text.schema.mismatch.mode=truncate时,修改列操作后

    • 符合修改后的Schema结构的数据,可以正常读取;

    • 存量旧Schema数据没有进行修改列操作,该表按新Schema读。

      例如,表添加列后,历史数据没有添加对应列,读表时该列的历史数据会补为NULL。

  • odps.sql.text.schema.mismatch.mode=ignore时,修改列操作后

    • 符合修改后的Schema结构的数据,可以正常读取;

    • 存量旧Schema数据没有进行修改列操作,该表按新Schema读。

      例如,表添加列后,历史数据没有添加对应列,读表时会把历史数据整行丢弃。

  • odps.sql.text.schema.mismatch.mode=error时,修改列操作后

    • 符合修改后的Schema结构的数据,可以正常读取;

    • 存量旧Schema数据没有进行修改列操作,该表按新Schema读。

      例如,表添加列后,历史数据没有添加对应列,读表时会报错。

创建外部表

语法结构

内置文本数据解析器

CSV格式

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格式

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>',...)];

内置开源数据解析器

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>',...)];

公共参数

公共参数说明请参见基础语法参数说明

独有参数

with serdeproperties属性参数

适用解析器

property_name

使用场景

说明

property_value

默认值

内置文本数据解析器(CsvStorageHandler/TsvStorageHandler)

odps.text.option.gzip.input.enabled

当需要读取以GZIP方式压缩的CSVTSV文件数据时,请添加该属性。

CSV、TSV压缩属性。配置该参数值为True时,MaxCompute才可以正常读取压缩文件,否则会读取失败。

  • True

  • False

False

odps.text.option.gzip.output.enabled

当需要将数据以GZIP压缩方式写入OSS时,请添加该属性。

CSV、TSV压缩属性。配置该参数值为True时,MaxCompute才能将数据以GZIP压缩方式写入OSS,否则不压缩。

  • True

  • False

False

odps.text.option.header.lines.count

OSS数据文件为CSVTSV,且需要忽略OSS数据文件中的前N行时,请添加该属性。

MaxCompute读取OSS数据文件时,会忽略指定的行数。

非负整数

0

odps.text.option.null.indicator

OSS数据文件为CSVTSV,且需要定义OSS数据文件中NULL的解析规则时,请添加该属性。

通过该参数配置的字符串会被解析为SQL中的NULL。

例如将文件中\N解析为NULL,需要配置此参数为\\N,其中第一个\为转义符。则a,\N,b会解析为a, NULL, b

字符串

空字符串

odps.text.option.ignore.empty.lines

OSS数据文件为CSVTSV,且需要定义OSS数据文件中空行的处理规则时,请添加该属性。

配置该参数值为True时,MaxCompute会忽略数据文件中的空行,否则会读取空行。

  • True

  • False

True

odps.text.option.encoding

OSS数据文件为CSVTSV,且OSS数据文件编码规则非默认编码规则时,请添加该属性。

确保此处配置的编码规则与OSS数据文件编码规则保持一致,否则MaxCompute无法成功读取数据。

  • UTF-8

  • UTF-16

  • US-ASCII

  • GBK

UTF-8

odps.text.option.delimiter

当需要明确CSVTSV数据文件的列分隔符时,请添加该属性。

确保此处配置的列分隔符可以正确读取OSS数据文件的每一列,否则MaxCompute读取的数据会出现错位问题。

单个字符

半角逗号(,)

odps.text.option.use.quote

CSVTSV数据文件中的字段包含换行(CRLF)、双引号或英文逗号时,请添加该属性。

CSV某个字段中包含换行、双引号(需要在"前再加"转义)或英文逗号时,整个字段必须用双引号("")括起来作为列分隔符。该参数指定是否识别CSV的列分隔符"

  • True

  • False

False

odps.sql.text.option.flush.header

在往OSS写数据的时候,文件块的第一行为表头。

只有针对CSV文件格式生效。

  • True

  • False

False

odps.sql.text.schema.mismatch.mode

当读取的OSS文件的数据列数和外部表的Schema列数不一致时。

指定对于列数不一致行的处理方式。

说明:odps.text.option.use.quote参数值为True时,该功能不生效。

  • error:报错。

  • truncate:超过外部表列数部分数据被丢弃;少于外部表列数部分数据则补null

  • ignore:丢弃不一致的行。

error

内置开源数据解析器(OpenCSVSerde)

separatorChar

当需要明确以TEXTFILE格式保存的CSV数据的列分隔符时,请添加该属性。

指定CSV数据列分隔符。

单个字符串

半角逗号(,)

quoteChar

当以TEXTFILE格式保存的CSV数据中的字段包含换行、双引号或英文逗号时,请添加该属性。

指定CSV数据的引用符。

单个字符串

escapeChar

当需要明确以TEXTFILE格式保存的CSV数据的转义规则时,请添加该属性。

指定CSV数据的转义符。

单个字符串

tblproperties属性参数

适用解析器

property_name

使用场景

说明

property_value

默认值

内置开源数据解析器(OpenCSVSerde)

skip.header.line.count

当需要忽略以TEXTFILE格式保存的CSV文件中的前N行数据时,请添加该属性。

MaxCompute读取OSS数据时,会忽略从首行开始指定行数的数据。

非负整数

skip.footer.line.count

当需要忽略以TEXTFILE格式保存的CSV文件中的后N行数据时,请添加该属性。

MaxCompute读取OSS数据时,会忽略从尾行开始指定行数的数据。

非负整数

mcfed.mapreduce.output.fileoutputformat.compress

当需要将TEXTFILE数据文件以压缩方式写入OSS时,请添加该属性。

TEXTFILE压缩属性。配置该参数值为True时,MaxCompute才可以将TEXTFILE数据文件以压缩方式写入OSS,否则不压缩。

  • True

  • False

False

mcfed.mapreduce.output.fileoutputformat.compress.codec

当需要将TEXTFILE数据文件以压缩方式写入OSS时,请添加该属性。

TEXTFILE压缩属性。设置TEXTFILE数据文件的压缩方式。

说明:只支持property_value中的四种压缩方式。

  • com.hadoop.compression.lzo.LzoCodec

  • com.hadoop.compression.lzo.LzopCodec

  • org.apache.hadoop.io.compress.SnappyCodec

  • com.aliyun.odps.io.compress.SnappyRawCodec

io.compression.codecs

OSS数据文件为Raw-Snappy格式时,请添加该属性。

配置该参数值为True时,MaxCompute才可以正常读取压缩数据,否则MaxCompute无法成功读取数据。

com.aliyun.odps.io.compress.SnappyRawCodec

odps.text.option.bad.row.skipping

OSS数据文件为CSV时,且需要跳过OSS数据文件中的脏数据时,请添加该属性。

MaxCompute读取OSS数据文件时,可以选择跳过或者不跳过脏数据。

  • rigid:一定会执行跳过逻辑,不能被session/project层面的配置覆盖。

  • flexible:数据层面开启弹性跳过,但也可以被session/project层面的配置覆盖。

写入数据

MaxCompute写入语法详情,请参见写入语法说明

查询分析

BadRowSkipping

CSV数据中出现脏数据时,可以通过BadRowSkipping功能,设置参数选择跳过或者不跳过报错数据。参数开启与否,不影响理解底层数据格式。

参数配置

  • 表级别参数odps.text.option.bad.row.skipping

    • rigid:一定要执行跳过逻辑,不能被session/project层面的配置覆盖。

    • flexible:数据层面开启弹性跳过,但也可以被session/project层面的配置覆盖。

  • session/project级别参数

    • odps.sql.unstructured.text.bad.row.skipping,可以覆盖flexible表参数,但不能覆盖rigid表参数。

      • on主动打开,如果表上未配置,自动打开。

      • off主动关闭,如果表上配置成flexible,会执行主动关闭,其他情况以表参数为准。

      • <null>/输入不合法时:只看表级别配置行为。

    • odps.sql.unstructured.text.bad.row.skipping.debug.num,表示允许打印出错结果到LogviewStdout中的条数。

      • 最大值只能为1000

      • 值<=0时,表示关闭

      • 输入不合法时,表示关闭

  • Session级别参数和表层面参数交互关系

    tbl property

    session flag

    result

    rigid

    on

    开,强制开启

    off

    <null>,或者不合法的值,或者不配置该参数值

    flexible

    on

    off

    关,session关闭

    <null>,或者不合法的值,或者不配置该参数值

    没有配置

    on

    开,session开启

    off

    <null>,或者不合法的值,或者不配置该参数值

使用示例

  1. 数据准备

    将存在部分脏数据的测试数据csv_bad_row_skipping.csv上传至OSSoss-mc-test/badrow/

  2. 创建CSV外部表

    表级别参数和session级别flag不一样,三种情况如下所示。

    • 表参数: odps.text.option.bad.row.skipping = flexible/rigid/<不填>

    • session flag: odps.sql.unstructured.text.bad.row.skipping = on/off/<不填>

    表级别没有配置参数

    -- 表级别没有配置参数,该报错就报错,靠session级别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://databucketpath>';

    表级别参数跳过错误行,Session级别可以关闭

    -- 表级别参数跳过错误行,不过session级别的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://databucketpath>'
    tblproperties (
      'odps.text.option.bad.row.skipping' = 'flexible'   -- 弹性开启,session级别可关闭
    );

    表级别参数跳过错误行,Session级别无法关闭

    -- 表级别参数强制开启跳过错误,session级别无法关闭
    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://databucketpath>'
    tblproperties (
      'odps.text.option.bad.row.skipping' = 'rigid'  -- 强制开启
    );
  3. 验证查询结果

    表级别没有配置参数

    -- session级别开启
    SET odps.sql.unstructured.text.bad.row.skipping=on;
    
    -- session级别关闭,如果表级别配置为'flexible',则会被关闭,如果表级别配置'rigid'则不受影响;
    SET odps.sql.unstructured.text.bad.row.skipping=off;
    
    -- session级别打印有问题的行数据,最大1000条,小于等于0时,就关闭了错误数据打印
    SET odps.sql.unstructured.text.bad.row.skipping.debug.num=10;
    
    SELECT * FROM test_csv_bad_data_skipping_flag;

    返回报错:FAILED: ODPS-0123131:User defined function exception

    表级别参数跳过错误行,Session级别可以关闭

    -- session级别开启
    SET odps.sql.unstructured.text.bad.row.skipping=on;
    
    -- session级别关闭,如果表级别配置为'flexible',则会被关闭,如果表级别配置'rigid'则不受影响;
    SET odps.sql.unstructured.text.bad.row.skipping=off;
    
    -- session级别打印有问题的行数据,最大1000条,小于等于0时,就关闭了错误数据打印
    SET odps.sql.unstructured.text.bad.row.skipping.debug.num=10;
    
    SELECT * FROM test_csv_bad_data_skipping_flexible;

    返回报错:FAILED: ODPS-0123131:User defined function exception

    表级别参数跳过错误行,Session级别无法关闭

    -- session级别开启
    SET odps.sql.unstructured.text.bad.row.skipping=on;
    
    -- session级别关闭,如果表级别配置为'flexible',则会被关闭,如果表级别配置'rigid'则不受影响;
    SET odps.sql.unstructured.text.bad.row.skipping=off;
    
    -- session级别打印有问题的行数据,最大1000条,小于等于0时,就关闭了错误数据打印
    SET odps.sql.unstructured.text.bad.row.skipping.debug.num=10;
    
    SELECT * FROM test_csv_bad_data_skipping_rigid;

    返回结果如下:

    +------------+------------+
    | a          | b          | 
    +------------+------------+
    | 1          | 26         | 
    | 5          | 37         | 
    +------------+------------+

Smart Parse更灵活的类型兼容能力

针对CSV格式的OSS外部表,MaxCompute SQL采用2.0数据类型进行读写,之前仅支持部分严格格式的值,并按照相应的数据类型进行读写。现已兼容读取CSV中各种类型的值。具体如下:

Type

Input as String

Output as String

Description

BOOLEAN

  • "true"/"false"

  • "T"/"F"

  • "1"/"0"

  • "Yes"/"No"

  • "Y"/"N"

  • ""(空串读进表中会被解析为NULL)

说明

解析时会对输入执行trim()操作。

  • "true"/"false"

  • "true"/"false"

  • "true"/"false"

  • "true"/"false"

  • "true"/"false"

  • ""(NULL被输出至CSV中为空串)

不在枚举值范围,则解析失败。

TINYINT

  • "0"

  • "1"

  • "-100"

  • "1,234,567"(千分位表示法,逗号不能作为该数字串的首尾字符)

  • "1_234_567"(Java-style,下划线不能作为该数字串的首尾字符)

  • "0.3e2"(科学计数法,其表示的实际数值为整数时可被解析,否则会报错)

  • "-1e5"(科学计数法)

  • "0xff"(十六进制,大小写不敏感)

  • "0b1001"(二进制,大小写不敏感)

  • "4/2"(分数,其表示的实际数值为整数时可被解析,否则会报错)

  • "1000%"(百分数,其表示的实际数值为整数时可被解析,否则会报错)

  • "1000‰"(千分数,其表示的实际数值为整数时可被解析,否则会报错)

  • "1,000 $"(带货币符号)

  • "$ 1,000"(带货币符号)

  • "3M"(K8s-style, 1000进制单位)

  • "2Gi"(K8s-style, 1024进制单位)

  • ""(空串读进表中会被解析为NULL)

说明
  • 解析时会对输入执行trim()操作。

  • 如果使用千分位表示法,例如"1,234,567" ,则需要手动设置CSV分隔符为非英文逗号的其他符号。具体请参见with serdeproperties属性参数odps.text.option.delimiter的用法。

  • K8s-style支持的1000进制单位包括 K/M/G/P/T,1024进制单位包括 Ki/Mi/Gi/Pi/Ti,详情请参见resource-management

  • 货币符号包括$/¥/€/£/₩/USD/CNY/EUR/GBP/JPY/KRW/IDR/RP

  • "0""1""-100"""naive模式下也可以被正确解析。

  • "0"

  • "1"

  • "-100"

  • "1234567"

  • "1234567"

  • "30"

  • "-100000"

  • "255"

  • "9"

  • "2"

  • "10"

  • "1"

  • "1000"

  • "1000"

  • "3000000"(1M=1000*1000)

  • "2147483648"(1 Gi=1024*1024*1024)

  • ""(NULL被输出至CSV中为空串)

8位整型,超出值域范围[-128, 127]则报错。

SMALLINT

16位整型,超出值域范围[-32768, 32767]则报错。

INT

32位整型,超出值域范围[-2147483648, 2147483647]则报错。

BIGINT

64位整型,超出值域范围[-9223372036854775807, 9223372036854775807]则报错。

说明

-263(即-9223372036854775808)不在值域范围内,为SQL引擎的限制。

FLOAT

  • "3.14"

  • "0.314e1"(科学计数法)

  • "2/5"(分数)

  • "123.45%"(百分数)

  • "123.45‰"(千分数)

  • "1,234,567.89"(千分位表示)

  • "1,234.56 $"(带货币符号)

  • "$ 1,234.56"(带货币符号)

  • "1.2M"(K8s-style,1000进制单位)

  • "2Gi"(K8s-style,1024进制单位)

  • "NaN"(大小写不敏感)

  • "Inf"(大小写不敏感)

  • "-Inf"(大小写不敏感)

  • "Infinity"(大小写不敏感)

  • "-Infinity"(大小写不敏感)

  • ""(空串读进表中会被解析为NULL)

说明
  • 解析时会对输入执行trim()操作。

  • "3.14""0.314e1""NaN""Infinity""-Infinity"""naive模式下也可以被正确解析。

  • "3.14"

  • "3.14"

  • "0.4"

  • "1.2345"

  • "0.12345"

  • "1234567.89"

  • "1234.56"

  • "1234.56"

  • "1200000"

  • "2147483648"

  • "NaN"

  • "Infinity"

  • "-Infinity"

  • "Infinity"

  • "-Infinity"

  • ""(null被输出至csv中为空串)

特殊值(大小写不敏感)包括:NaN、Inf、-Inf、Intifniy和-Intifniy。在超出值域范围时,将报错;若精度超出,则进行四舍五入截断。

DOUBLE

  • "3.1415926"

  • "0.314e1"(科学计数法)

  • "2/5"(分数)

  • "123.45%"(百分数)

  • "123.45‰"(千分数)

  • "1,234,567.89"(千分位表示)

  • "1,234.56 $"(带货币符号)

  • "$ 1,234.56"(带货币符号)

  • "1.2M"(K8s-style,1000进制单位)

  • "2Gi"(K8s-style,1024进制单位)

  • "NaN"(大小写不敏感)

  • "Inf"(大小写不敏感)

  • "-Inf"(大小写不敏感)

  • "Infinity"(大小写不敏感)

  • "-Infinity"(大小写不敏感)

  • ""(空串读进表中会被解析为NULL)

说明
  • 解析时会对输入执行trim()操作。

  • "3.1415926""0.314e1""NaN""Infinity""-Infinity"""naive模式下也可以被正确解析。

  • "3.1415926"

  • "3.14"

  • "0.4"

  • "1.2345"

  • "0.12345"

  • "1234567.89"

  • "1234.56"

  • "1234.56"

  • "1200000"

  • "2147483648"

  • "NaN"

  • "Infinity"

  • "-Infinity"

  • "Infinity"

  • "-Infinity"

  • ""(NULL被输出至CSV中为空串)

特殊值(大小写不敏感)包括:NaN、Inf、-Inf、Intifniy和-Intifniy。在超出值域范围时,将报错;若精度超出,则进行四舍五入截断。

DECIMAL

(precision, scale)

DECIMAL(15,2)为例

  • "3.358"

  • "2/5"(分数)

  • "123.45%"(百分数)

  • "123.45‰"(千分数)

  • "1,234,567.89"(千分位表示)

  • "1,234.56 $"(带货币符号)

  • "$ 1,234.56"(带货币符号)

  • "1.2M"(K8s-style, 1000进制单位)

  • "2Gi"(K8s-style, 1024进制单位)

  • ""(空串读进表中会被解析为NULL)

说明
  • 解析时会对输入执行trim()操作。

  • "3.358"""naive模式下也可以被正确解析。

  • "3.36"(四舍五入)

  • "0.4"

  • "1.23"(四舍五入)

  • "0.12"(四舍五入)

  • "1234567.89"

  • "1234.56"

  • "1234.56"

  • "1200000"

  • "2147483648"

  • ""(NULL被输出至CSV中为空串)

整数部分值超出precision-scale

则会报错;小数部分超出scale,则会进行四舍五入截断。

CHAR(n)

CHAR(7)为例

  • "abcdefg"

  • "abcdefghijklmn"

  • "abc"

  • ""(空串读进表中会被解析为NULL)

  • "abcdefg"

  • "abcdefg"(后续部分被截断)

  • "abc____"(_表示空格,扩充了4个空字符)

  • ""(NULL被输出至CSV中为空串)

最大长度为255。长度不足则会填充空格,但空格不参与比较。长度超出n会截断。

VARCHAR(n)

VARCHAR(7)为例

  • "abcdefg"

  • "abcdefghijklmn"

  • "abc"

  • ""(空串读进表中会被解析为NULL)

  • "abcdefg"

  • "abcdefg"(后续部分被截断)

  • "abc"

  • ""(NULL被输出至CSV中为空串)

最大长度为65535。长度超出n会截断。

STRING

  • "abcdefg"

  • "abc"

  • ""(空串读进表中会被解析为NULL)

  • "abcdefg"

  • "abc"

  • ""(NULL被输出至CSV中为空串)

长度限制为8 MB。

DATE

  • "yyyy-MM-dd"(例如"2025-02-21")

  • "yyyyMMdd"(例如"20250221")

  • "MMM d,yyyy"(例如"October 1,2025")

  • "MMMM d,yyyy"(例如"Oct 1,2025")

  • "yyyyMd日"(例如"2025101日")

  • ""(空串读进表中会被解析为NULL)

说明

您也可以通过自定义odps.text.option.date.io.format控制读入时的解析方式。例如用户定义format'dd/MM/yyyy#yyyy--MM--dd',则可以解析'21/02/2025''2025--02--21'

  • "2000-01-01"

  • "2000-01-01"

  • ""(NULL被输出至CSV中为空串)

  • 无时分秒信息,改变时区不影响输出结果。默认输出格式为"yyyy-MM-dd"

  • 您也可以通过自定义odps.text.option.date.io.format 来控制解析与输出格式。输出格式将采用自定义的首个pattern。格式可参考DateTimeFormatter

TIMESTAMP_NTZ

说明

OpenCsvSerde不支持此类型,因为该类型与Hive数据格式不兼容。

  • 纳秒位长度可为0~9,MaxCompute支持的内置解析格式如下:

    • "yyyy-MM-dd HH:mm:ss[.SSSSSSSSS]"(例如"2000-01-01 00:00:00.123")

    • "yyyy-MM-ddTHH:mm:ss[.SSSSSSSSS]"(例如"2000-01-01T00:00:00.123456789")

    • "yyyyMMddHHmmss"(例如"20000101000000")

    • ""(空串读进表中会被解析为NULL)

  • 您也可通过自定义odps.text.option.timestamp_ntz.io.format来控制读入时的解析方式。例如用户定义format'ddMMyyyy-HHmmss',则可以解析形如'31102024-103055'的字符串。

  • "2000-01-01 00:00:00.123000000"

  • "2000-01-01 00:00:00.123456789"

  • "2000-01-01 00:00:00.000000000"

  • ""(NULL被输出至CSV中为空串)

  • 本质为纳秒粒度的时间戳,改变时区信息不影响输出结果,即默认使用标准UTC时区输出结果。默认输出格式为"yyyy-MM-dd HH:mm:ss.SSSSSSSSS"

  • 您也可以通过自定义odps.text.option.timestamp_ntz.io.format控制解析与输出格式。格式可参考DateTimeFormatter

DATETIME

  • 毫秒位长度可为0~3,x表示时区偏移,假设系统时区为Asia/Shanghai,MaxCompute支持的内置解析格式如下:

    • "yyyy-MM-dd HH:mm:ss[.SSS][x]"(例如"2000-01-01 00:00:00.123")

    • "yyyy-MM-ddTHH:mm:ss[.SSS][x]"(例如"2000-01-01T00:00:00.123+0000")

    • "yyyyMMddHHmmss[x]" (例如"20000101000000+0000")

    • ""(空串读进表中会被解析为NULL)

  • 您也可通过自定义odps.text.option.datetime.io.format来控制读入时的解析方式。例如用户定义format'yyyyMMdd-HHmmss.SSS',则可以解析形如'20241031-103055.123'的字符串。

假设系统时区为Asia/Shanghai:

  • "2000-01-01 00:00:00.123+0800"

  • "2000-01-01 08:00:00.123+0800"

  • "2000-01-01 08:00:00.000+0800"

  • ""(NULL被输出至CSV中为空串)

  • 本质为毫秒粒度的时间戳,改变时区信息影响输出结果。默认输出格式为"yyyy-MM-dd HH:mm:ss.SSSx"

  • 您可通过odps.sql.timezone修改系统时区,从而控制写出值的时区偏移。

  • 您也可通过自定义odps.text.option.datetime.io.format控制解析与输出格式。格式可参考:DateTimeFormatter

TIMESTAMP

  • 纳秒位长度可为0~9,x表示时区偏移,假设系统时区为Asia/Shanghai,MaxCompute支持的内置解析格式:

    • "yyyy-MM-dd HH:mm:ss[.SSSSSSSSS][x]"(例如"2000-01-01 00:00:00.123456")

    • "yyyy-MM-ddTHH:mm:ss[.SSSSSSSSS][x]"(例如"2000-01-01T00:00:00.123+0000")

    • "yyyyMMddHHmmss[x]"(例如"20000101000000+0000")

    • ""(空串读进表中会被解析为NULL)

  • 您也可通过自定义odps.text.option.timestamp.io.format来控制读入时的解析方式。例如用户定义format'yyyyMMdd-HHmmss',则可以解析形如'20240910-103055'的字符串。

(假设系统时区为Asia/Shanghai)

  • "2000-01-01 00:00:00.123456000+0800"

  • "2000-01-01 08:00:00.123000000+0800"

  • "2000-01-01 08:00:00.000000000+0800"

  • ""(NULL被输出至CSV中为空串)

  • 本质为纳秒粒度的时间戳,改变时区信息影响输出结果。默认输出格式为"yyyy-MM-dd HH:mm:ss.SSSSSSSSSx"

  • 您可通过odps.sql.timezone修改系统时区,从而控制写出值的时区偏移。

  • 您也可通过自定义odps.text.option.timestamp.io.format控制解析与输出格式。格式可参考DateTimeFormatter

  • 通用规则

    • 对于任意类型,CSV数据文件中的空字符串被读入表中后,均会被解析为NULL。

  • 不支持的数据类型

    • 复杂类型 (STRUCT/ARRAY/MAP):不支持。因为这些复杂类型的值很容易和CSV的常见分隔符(,)发生冲突导致解析失败。

    • BINARY/INTERVAL 类型:暂不支持。如有需求请联系MaxCompute技术支持。

  • 数值类型 (INT、DOUBLE等)

    • 对于数据类型INT/SMALLINT/TINYINT/BIGINT/FLOAT/DOUBLE/DECIMAL,默认的解析能力(DEFAULT)比较丰富。

    • 如果只想解析基础的数字串,可以在tblproperties中将odps.text.option.smart.parse.level参数设置为naive,使解析器仅支持解析普通数字串,例如“123”、“123.456”,其他形态的字符串在解析时会报错。

  • 日期与时间类型 (DATE, TIMESTAMP等)

    • 对于四种日期或时间相关类型 DATA/DATETIME/TIMESTAMP/TIMESTAMP_NTZ,在底层代码中均使用 java.time.format.DateTimeFormatter 处理。

    • 默认格式:MaxCompute内置了若干的默认解析格式。

    • 自定义格式:

      • 通过在 tblproperties 中设置 odps.text.option.<date|datetime|timestamp|timestamp_ntz>.io.format 来自定义多种解析格式与一种输出格式。

      • 多个解析格式之间用 # 分隔。

      • 自定义格式的优先级高于MaxCompute的内置解析格式,输出格式将采用第一个自定义格式。

      • 示例:若自定义DATE类型的格式串为pattern1#pattern2#pattern3,则可以解析符合pattern1pattern2pattern3的字符串,但输出至文件时会采用pattern1格式来输出。详情请参见DateTimeFormatter

  • 重要提醒:关于时区格式z的说明

    • 不建议使用 'z' (time-zone name) 作为自定义的时区格式(尤其对于中国区用户),因为 'z' 在某些上下文中可能存在歧义。

    • 建议改用 'x' (zone-offset) 或 'VV' (time-zone ID) 作为时区模式。

    • 示例:'CST' 在中国语境下通常表示中国标准时间(UTC+8),然而当 'CST' 被java.time.format.DateTimeFormatter解析时,会被识别为美国中部标准时间(UTC-6),这可能导致非预期的输入或输出结果。

使用示例

前置准备

  • 创建MaxCompute项目

  • 已准备好OSS存储空间(Bucket)、OSS目录。具体操作请参见创建存储空间管理目录

    MaxCompute已支持在OSS侧自动创建目录,对于携带外部表及UDFSQL语句,可以通过一条SQL语句执行读写外部表及UDF的操作。原手动创建目录方式仍然支持。

    由于MaxCompute只在部分地域部署,跨地域的数据连通性可能存在问题,因此建议BucketMaxCompute项目所在地域保持一致。
  • 授权

    • 已具备访问OSS的权限。阿里云账号(主账号)、RAM用户或RAMRole身份可以访问OSS外部表,授权信息请参见OSSSTS模式授权

    • 已具备在MaxCompute项目中创建表(CreateTable)的权限。表操作的权限信息请参见MaxCompute权限

通过内置文本数据解析器创建OSS外部表

示例一:非分区表

  1. 示例数据中的Demo1/目录建立映射关系。创建OSS外部表的命令示例如下。

    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/';
    
    -- 可以执行desc extended mc_oss_csv_external1;命令查看创建好的OSS外部表结构信息。

    示例中使用的角色为aliyunodpsdefaultrole,如果使用其他角色,需要将aliyunodpsdefaultrole替换为目标角色名称,并为目标角色授予访问OSS的权限。

  2. 查询非分区外表。

    SELECT * FROM mc_oss_csv_external1;

    返回结果如下:

    +------------+------------+------------+------------+------------------+-------------------+----------------+------------+
    | 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          |
    +------------+------------+------------+------------+------------------+-------------------+------------+----------------+
  3. 写入数据至非分区外表,并查看数据是否已成功写入。

    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;

    返回结果如下:

    +------------+------------+------------+------------+------------------+-------------------+----------------+------------+
    | vehicleid  | recordid   | patientid  | calls      | locationlatitute | locationlongtitue | recordtime     | direction  |
    +------------+------------+------------+------------+------------------+-------------------+----------------+------------+
    | 1          | 12         | 76         | 1          | 46.81006         | -92.08174         | 9/14/2014 0:10 | SW         |
    +------------+------------+------------+------------+------------------+-------------------+----------------+------------+

    查看OSS路径Demo1/下已产生新文件。image

示例二:分区表

  1. 示例数据中的Demo2/目录建立映射关系。创建OSS外部表并引入分区数据的命令示例如下。

    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/';
    
    -- 可以执行DESC EXTENDED mc_oss_csv_external2;命令查看创建好的外部表结构信息。

    示例中使用的角色为aliyunodpsdefaultrole,如果使用其他角色,需要将aliyunodpsdefaultrole替换为目标角色名称,并为目标角色授予访问OSS的权限。

  2. 引入分区数据。当创建的OSS外部表为分区表时,需要额外执行引入分区数据的操作,更多操作请参见补全OSS外部表分区数据语法

    MSCK REPAIR TABLE mc_oss_csv_external2 ADD PARTITIONS;
    
    -- 等效于如下语句。
    ALTER TABLE mc_oss_csv_external2 ADD PARTITION (direction = 'N') 
      PARTITION (direction = 'NE') PARTITION (direction = 'S') 
      PARTITION (direction = 'SW') PARTITION (direction = 'W');
  3. 查询分区外表。

    SELECT * FROM mc_oss_csv_external2 WHERE direction='NE';

    返回结果如下:

    +------------+------------+------------+------------+------------------+-------------------+----------------+------------+
    | 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         |
    +------------+------------+------------+------------+------------------+-------------------+----------------+------------+
  4. 写入数据至分区外表,并查看是否已成功写入。

    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;

    返回结果如下:

    +------------+------------+------------+------------+------------------+-------------------+----------------+------------+
    | vehicleid  | recordid   | patientid  | calls      | locationlatitute | locationlongtitue | recordtime     | direction  |
    +------------+------------+------------+------------+------------------+-------------------+----------------+------------+
    | 1          | 12         | 76         | 1          | 46.81006         | -92.08174         | 9/14/2014 0:10 | NE         |
    +------------+------------+------------+------------+------------------+-------------------+----------------+------------+

    查看OSS路径Demo2/direction=NE下已产生新文件。image

示例三:压缩数据

本示例创建以GZIP压缩的CSV格式外表,进行数据读取和写入操作。

  1. 创建内部表并写入测试数据,用来进行后续的写入测试。

    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');
  2. 创建以GZIP压缩的CSV格式外表,与示例数据中的Demo3/(压缩数据)目录建立映射关系。创建OSS外部表命令示例如下。

    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/';
    
    -- 引入分区数据
    MSCK REPAIR TABLE mc_oss_csv_external3 ADD PARTITIONS;
    
    -- 可以执行DESC EXTENDED mc_oss_csv_external3;命令查看创建好的外部表结构信息。

    示例中使用的角色为aliyunodpsdefaultrole,如果使用其他角色,需要将aliyunodpsdefaultrole替换为目标角色名称,并为目标角色授予访问OSS的权限。

  3. 使用MaxCompute客户端,从OSS中读取数据。命令示例如下:

    说明

    如果OSS压缩数据为开源数据格式,需要在SQL语句前添加set odps.sql.hive.compatible=true;命令,与SQL语句一起提交执行。

    --开启全表扫描,仅此Session有效。
    SET odps.sql.allow.fullscan=true;
    SELECT recordId, patientId, direction FROM mc_oss_csv_external3 WHERE patientId > 25;

    返回结果如下:

    +------------+------------+------------+
    | recordid   | patientid  | direction  |
    +------------+------------+------------+
    | 1          | 51         | S          |
    | 3          | 48         | NE         |
    | 4          | 30         | W          |
    | 5          | 47         | S          |
    | 7          | 53         | N          |
    | 8          | 63         | SW         |
    | 10         | 31         | N          |
    +------------+------------+------------+
  4. 读取内部表的数据写入至OSS外表。

    通过MaxCompute客户端对外部表执行INSERT OVERWRITEINSERT INTO命令,将数据写入OSS。

    INSERT INTO TABLE mc_oss_csv_external3 PARTITION (dt='20250418') SELECT * FROM vehicle_test;

    执行成功后,可在OSS目录下查看导出的文件。

创建OSS外部表并指定对应OSS文件的第一行为表头

示例数据oss-mc-testBucket下创建Demo11目录,并执行如下语句:

--创建外部表
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 OVERWRITE TABLE mf_oss_wtt VALUES (1, 'val1', 1.1),(2, 'value2', 1.3);

--查询数据
--在建表的时候可以把所有字段建成string,否则表头读取时会报错。
--或者在建表的时候需要加跳过表头的参数:'odps.text.option.header.lines.count' = '1'
SELECT * FROM mf_oss_wtt;

示例中使用的角色为aliyunodpsdefaultrole,如果使用其他角色,需要将aliyunodpsdefaultrole替换为目标角色名称,并为目标角色授予访问OSS的权限。

返回结果如下:

+----------+--------+------------+
| id       | name   | tran_amt   |
+----------+--------+------------+
| 1        | val1   | 1.1        |
| 2        | value2 | 1.3        |
+----------+--------+------------+

创建OSS外部表且外部表列数与OSS数据列数不一致

  1. 示例数据oss-mc-testBucket下创建demo目录,并上传test.csv文件。test.csv文件内容如下。

    1,kyle1,this is desc1
    2,kyle2,this is desc2,this is two
    3,kyle3,this is desc3,this is three, I have 4 columns
  2. 创建外部表。

    1. 指定对于列数不一致的行的处理方式为TRUNCATE

      -- 删除表
      DROP TABLE test_mismatch;
      -- 新建外部表
      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/';
    2. 指定对于列数不一致的行的处理方式为IGNORE

      -- 删除表
      DROP TABLE test_mismatch01;
      -- 新建外部表
      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/';
    3. 查询表数据。

      • 查询test_mismatch表。

        SELECT * FROM test_mismatch;
        
        --返回结果
        +----+-------+---------------+---------------+
        | 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 |
        +----+-------+---------------+---------------+
      • 查询test_mismatch01表。

        SELECT * FROM test_mismatch01;
        
        --返回结果
        +----+-------+----------------+-------------+
        | id | name  | dect           | col4        |
        +----+-------+----------------+-------------+
        | 2  | kyle2 | this is desc2  | this is two  
        +----+-------+----------------+-------------+

通过内置开源解析器创建OSS外部表

通过内置开源解析器创建OSS外部表读取以逗号为分隔符的文件,并忽略首行和尾行的数据。

  1. 示例数据oss-mc-testBucket下创建demo-test目录,并上传测试文件test.csv

    测试文件包含的数据信息如下。

    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
  2. 创建外部表,并指定分隔符为逗号,设置忽略首行和尾行参数。

    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/***/'
    -- 设置忽略首行和尾行参数。
    TBLPROPERTIES (
    "skip.header.line.COUNT"="1",
    "skip.footer.line.COUNT"="1"
    )
    ;
  3. 读取外部表。

    SELECT * FROM ext_csv_test08;
    
    -- 返回结果,只读了8条,忽略了首行和尾行。
    +------------+------------+------------+------------+------------------+-------------------+------------+------------+
    | 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          | 
    +------------+------------+------------+------------+------------------+-------------------+------------+------------+

创建CSV格式的OSS外部表-自定义各种时间数据类型

CSV自定义时间类型的解析与输出格式详情,请参见Smart Parse更灵活的类型兼容能力

  1. 创建各时间数据类型的CSV外表(datetime/timestamp/timestamp_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');
  2. 插入数据后,CSV文件的内容为:

    02/21/2025,2025-02-21-08-30-00 +08,2025-02-21 12-30-00 Asia/Shanghai,2025-02-21 16:30:00.12
  3. 再次读数据,可以看到结果。

    SELECT * FROM test_csv;

    返回结果如下:

    +------------+---------------------+---------------------+------------------------+
    | 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 |
    +------------+---------------------+---------------------+------------------------+

常见问题

读取CSV/TSV数据时会报错“列数不匹配”

  • 问题现象

    CSV/TSV文件中的列数与外表DDL中的列数量的不一致,读取CSV/TSV数据时会报错“列数不匹配”,例如FAILED: ODPS-0123131:User defined function exception - Traceback:java.lang.RuntimeException: SCHEMA MISMATCH:xxx

  • 解决方案

    通过在Session级别设置odps.sql.text.schema.mismatch.mode参数,可以控制输出结果:

    • SET odps.sql.text.schema.mismatch.mode=error:表示列数不一致时,系统直接报错。

    • SET odps.sql.text.schema.mismatch.mode=truncate:表示列数不一致时,超过外表DDL中的列数部分将被丢弃;少于外表DDL中的列数部分,列值将被补充为NULL。