如何在Impala中处理含引号内分隔符的CSV及创建CAB外部表
刚好处理过类似的场景,给你两个问题的具体解决方案:
其实Impala可以借助Hive的OpenCSVSerDe来完美处理这种场景——这个SerDe专门用来解析带引号、内部含分隔符的CSV文件,能正确识别被双引号包裹的字段,哪怕字段里有分隔符也不会误拆分。
直接给你建表的示例语句:
CREATE EXTERNAL TABLE IF NOT EXISTS csv_data_table ( col1 STRING, col2 STRING, col3 STRING -- 根据你的实际字段调整 ) ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde' WITH SERDEPROPERTIES ( "separatorChar" = ",", -- 替换成你的CSV实际分隔符 "quoteChar" = "\"", "escapeChar" = "\\" -- 如果字段内有转义的双引号(比如""),需要设置这个 ) STORED AS TEXTFILE LOCATION '/hdfs/path/to/your/csv/files';
要是你的Impala集群一开始识别不了这个SerDe,先在Hive里创建上面的表,然后在Impala中执行INVALIDATE METADATA csv_data_table;同步元数据,之后就能正常查询了。
如果环境限制导致Impala没法用SerDe,那可以试试这几个替代方案,都是实际工作中验证过的:
方案1:用Hive预处理后转成Impala友好的格式
先让Hive来做脏活累活,用OpenCSVSerDe解析CAB文件,然后把数据转成Parquet/ORC这种Impala高效读取的格式:
- 先在Hive里创建临时表解析CAB:
CREATE EXTERNAL TABLE hive_cab_staging ( id STRING, name STRING, details STRING -- 对应你的示例数据字段 ) ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde' WITH SERDEPROPERTIES ( "separatorChar" = "|", "quoteChar" = "\"", "escapeChar" = "\\" ) STORED AS TEXTFILE LOCATION '/hdfs/path/to/your/cab/files';
- 把解析好的数据导出到Parquet表:
CREATE TABLE hive_cab_parquet STORED AS PARQUET AS SELECT * FROM hive_cab_staging;
- 回到Impala,同步元数据后直接查询或者建外部表:
INVALIDATE METADATA hive_cab_parquet; -- 或者自己建Impala外部表指向Parquet目录 CREATE EXTERNAL TABLE impala_cab_table ( id STRING, name STRING, details STRING ) STORED AS PARQUET LOCATION '/user/hive/warehouse/hive_cab_parquet';
方案2:用Impala字符串函数手动解析(适合小数据量)
如果数据量不大,不想折腾Hive/Spark,可以用Impala的正则函数手动拆分。针对你给的示例数据123|Abhijit|"||a|b|",可以这么写:
首先建一个临时表读取整个行作为字符串:
CREATE EXTERNAL TABLE raw_cab_files ( raw_line STRING ) STORED AS TEXTFILE LOCATION '/hdfs/path/to/your/cab/files';
然后用regexp_extract提取每个字段:
SELECT regexp_extract(raw_line, '^(?:([^|]*?)\\|)?(?:([^|]*?)\\|)?(?:\"(.*?)\")?$', 1) AS id, regexp_extract(raw_line, '^(?:([^|]*?)\\|)?(?:([^|]*?)\\|)?(?:\"(.*?)\")?$', 2) AS name, regexp_extract(raw_line, '^(?:([^|]*?)\\|)?(?:([^|]*?)\\|)?(?:\"(.*?)\")?$', 3) AS details FROM raw_cab_files;
这个正则的逻辑是:先匹配前两个非管道的字段,然后匹配被双引号包裹的内容(不管里面有多少管道)。如果你的字段数量更多,需要调整正则的分组数量。
方案3:用Spark批量预处理
如果数据量较大,用Spark来处理更高效。Spark原生支持解析带引号、内部含分隔符的文本文件,处理后导出成Impala能直接读的格式:
Scala代码示例:
import org.apache.spark.sql.SparkSession val spark = SparkSession.builder() .appName("ProcessCABFiles") .enableHiveSupport() .getOrCreate() // 读取CAB文件,指定分隔符和引号规则 val cabDF = spark.read .option("sep", "|") .option("quote", "\"") .option("escape", "\\") .option("header", "false") // 如果你的文件没有表头就设为false .csv("/hdfs/path/to/your/cab/files") // 写入Parquet格式,供Impala读取 cabDF.write .mode("overwrite") .parquet("/hdfs/path/to/processed_cab_parquet")
之后在Impala中建外部表指向这个Parquet目录就可以了。
内容的提问来源于stack exchange,提问作者cloudnotguru

