You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ClickHouse读取Minio中Parquet文件时NULL值插入非空列报错求助

问题分析

错误CANNOT_INSERT_NULL_IN_ORDINARY_COLUMN的核心原因是:ClickHouse读取Parquet文件时,将accepted_at列解析为Nullable(UInt32)类型,但插入过程中试图将其转换为非Nullable的UInt32(而非表定义的Nullable(DateTime64(3))),这大概率是列顺序不匹配或类型自动推断偏差导致的,而非表结构本身的问题。

解决方案

1. 显式指定列映射与类型转换

避免使用SELECT *,手动对应Parquet列与目标表列,并对日期类列做显式类型转换,同时保留Nullable属性:

-- 开启按列名匹配(可选但推荐)
SET input_format_parquet_use_column_names = 1;

INSERT INTO general_marts.gm_order (
    order_id,
    user_id,
    vendor_id,
    total_price,
    total_price_after_discount,
    payable_price,
    customer_name,
    vendor_name,
    preparation_time,
    delivery_type,
    accepted_at,
    nfc_reason,
    rejected_at,
    paid_at,
    created_at,
    list_order_status,
    list_order_status_datetime,
    list_payment_status,
    list_payment_status_datetime,
    list_delivery_status,
    list_delivery_status_datetime,
    voucher_id,
    voucher_name,
    voucher_code,
    voucher_value,
    ofood_share_delivery_price,
    customer_share_delivery_price,
    vendor_share_delivery_price,
    delivery_order_id,
    packing_price,
    vendor_tax,
    refund_at,
    refund_amount,
    city_name,
    list_product_id,
    list_product_name,
    list_product_variation_id,
    list_product_variation_name,
    list_option_ids,
    list_options,
    list_option_label,
    list_has_option,
    list_price,
    list_price_after_discount,
    list_quantity,
    list_stock,
    list_capacity
)
SELECT
    order_id,
    user_id,
    vendor_id,
    total_price,
    total_price_after_discount,
    payable_price,
    customer_name,
    vendor_name,
    preparation_time,
    delivery_type,
    -- 将Nullable(UInt32)转换为表定义的Nullable(DateTime64(3))
    toNullable(toDateTime64(accepted_at, 3)),
    nfc_reason,
    toNullable(toDateTime64(rejected_at, 3)),
    toNullable(toDateTime64(paid_at, 3)),
    toDateTime64(created_at, 3),
    list_order_status,
    list_order_status_datetime,
    list_payment_status,
    list_payment_status_datetime,
    list_delivery_status,
    list_delivery_status_datetime,
    voucher_id,
    voucher_name,
    voucher_code,
    voucher_value,
    ofood_share_delivery_price,
    customer_share_delivery_price,
    vendor_share_delivery_price,
    delivery_order_id,
    packing_price,
    vendor_tax,
    toNullable(toDateTime64(refund_at, 3)),
    refund_amount,
    city_name,
    list_product_id,
    list_product_name,
    list_product_variation_id,
    list_product_variation_name,
    list_option_ids,
    list_options,
    list_option_label,
    list_has_option,
    list_price,
    list_price_after_discount,
    list_quantity,
    list_stock,
    list_capacity
FROM s3(
'http://host:9000/general-marts/general_mart_order/year=2024/month=02/day=12/*.snappy.parquet',
'aws_access_key_id',
'aws_secret_access_key','Parquet');

2. 调整ClickHouse的Parquet读取参数

通过配置参数让ClickHouse严格按列名匹配而非位置匹配,避免列顺序错位导致的类型错误:

-- 开启按列名匹配Parquet列
SET input_format_parquet_use_column_names = 1;
-- 允许Parquet与表之间存在缺失列(如果有需要)
SET input_format_parquet_allow_missing_columns = 1;

-- 再执行原INSERT语句
INSERT INTO general_marts.gm_order
SELECT *
FROM s3(
'http://host:9000/general-marts/general_mart_order/year=2024/month=02/day=12/*.snappy.parquet',
'aws_access_key_id',
'aws_secret_access_key','Parquet');

3. 修正Spark写入Parquet的类型映射

从根源上确保Spark写出的Parquet类型与ClickHouse表结构一致:

  • 对于日期类型,调整Spark写入格式,让ClickHouse能正确识别:
df.write
  .option("parquet.writeLegacyFormat", false) // 禁用旧格式,使用标准Parquet类型
  .option("timestampFormat", "yyyy-MM-dd'T'HH:mm:ss.SSS")
  .partitionBy("year", "month", "day")
  .parquet("s3a://general-marts/general_mart_order/")
  • 确保Spark中的Nullable列正确标记,避免写出时丢失Nullability信息。

内容的提问来源于stack exchange,提问作者Bahram Jannesar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 00:57:07