【问题标题】:How to import a CSV file with dot as thousand separator into PostgreSQL如何将带有点作为千位分隔符的 CSV 文件导入 PostgreSQL
【发布时间】:2021-04-11 21:12:19
【问题描述】:

我正在尝试将 CSV 文件读入 PostgreSQL。 CSV 文件共有 20 列,当我在 PostgreSQL 中创建空表时,除了一列(称为“neto”)之外,我对每一列都有正确的数据类型。如果您尝试使用 Excel 打开 CSV 文件并检查“neto”列,您会看到它具有如下数字数据类型:“-1.234,10”,举个例子。

首先我在 PostgreSQL 中创建一个空表:

CREATE TABLE invoices (
    FECHA TIMESTAMP,
    VENC TIMESTAMP,
    COMPANIA VARCHAR(100),
    FACTURA VARCHAR(100),
    GRUPO VARCHAR(100),
    CLIENTE VARCHAR(200),
    VENDEDOR VARCHAR(100),
    DESCRIP VARCHAR(300),
    SEGMENTO VARCHAR(100),
    MODELO VARCHAR(100),
    ARTICULO VARCHAR(200),
    CANTIDAD NUMERIC(100,50),
    UNIDAD DOUBLE PRECISION,
    ALMACEN DOUBLE PRECISION,
    PRECIO MONEY,
    IMPUESTO DOUBLE PRECISION,
    DESCUENTO VARCHAR(200),
    NETO MONEY,
    ANULADA VARCHAR(200),
    TASA DOUBLE PRECISION
);

然后,在尝试读取 CSV 后:

\copy invoices FROM 'C:/Users/Caproki/Desktop/INVOICES.CSV' DELIMITER ';' CSV HEADER;

我收到以下错误:

ERROR:  invalid input syntax for type money: "5.796,00"
CONTEXT:  COPY invoices, line 2, column neto: "5.796,00"

如您所见,由于我的本地设置,CSV文件实际上是用分号而不是逗号分隔的,因为逗号用于分隔每个数字的整数部分和小数部分,而点是用作千位分隔符。

进一步检查此 CSV 文件后,似乎问题是因为这个点用作千位分隔符(例如,您有“1.200,00”而不是“1200,00” )。当我在 CSV 文件中对此进行更正时,问题不再出现,我可以成功地将所有内容导入 PostregreSQL。

但是,我不愿意手动更改源 CSV 文件中列的数据类型,因为这是我每天都需要执行的导入过程。我想这样做是尽可能直接的,只是通过用SQL导入CSV文件,我不是在寻找中间程序或解决这个简单问题的方法。

有没有办法在 PostgreSQL 中指定点用作千位分隔符?我在官方文档和其他 StackOverflow 问题中都找不到任何内容。

顺便说一下,我已经尝试将“neto”列的数据类型更改为数字、双精度和所有其他数字类型,但并没有解决问题。

非常感谢。

【问题讨论】:

  • 如果您不想要 PostgreSQL 之外的解决方案,添加 Excel 标记并没有真正的好处。我会删除它。
  • @teylyn 谢谢。

标签: postgresql csv validation import import-from-excel


【解决方案1】:

你不能单独使用COPY 来做到这一点。它只适用于类型输入函数可以理解的格式,这里不是这样。

要么在加载之前在 PostgreSQL 之外修改文件,要么将其加载到在此位置有 text 列的表中,然后再修改表。

【讨论】:

    【解决方案2】:

    解决此类问题的一个非常简单的方法是

    1. 将数据导入临时表
    2. 按照您想要的格式进行“更正”
    3. 用新的格式化数据填充目标表

    示例

    数据样本 (file.csv)

    内托

    1.420,55

    8.666,42

    测试表

    CREATE TABLE t (neto money);
    CREATE TABLE t_temp (neto text);
    

    将 csv 文件导入临时表

    \copy t_temp from file.csv csv header delimiter ';'
    

    填充目标表

    INSERT INTO t  
    SELECT neto::money FROM t_temp;
    
    SELECT * FROM t;
    
        neto    
    ------------
     1.420,55 €
     8.666,42 €
    

    【讨论】:

      【解决方案3】:

      使用money 类型并将lc_monetary 选项设置为正确的语言环境:

      show lc_monetary;
      ┌─────────────┐
      │ lc_monetary │
      ├─────────────┤
      │ uk_UA.utf8  │
      └─────────────┘
      
      select 123456.78::money;
      ┌─────────────────┐
      │      money      │
      ├─────────────────┤
      │  123 456,78грн. │
      └─────────────────┘
      
      set lc_monetary to 'id_ID.utf8';
      select 123456.78::money;
      ┌──────────────┐
      │    money     │
      ├──────────────┤
      │ Rp123.456,78 │
      └──────────────┘
      
      
      create table t(x money);
      \copy t from stdin
      Enter data to be copied followed by a newline.
      End with a backslash and a period on a line by itself, or an EOF signal.
      >> 123.456,78
      >> 7.654,3
      >> \.
      COPY 2
      
      table t;
      ┌──────────────┐
      │      x       │
      ├──────────────┤
      │ Rp123.456,78 │
      │   Rp7.654,30 │
      └──────────────┘
      
      reset lc_monetary; show lc_monetary; table t;
      RESET
      ┌─────────────┐
      │ lc_monetary │
      ├─────────────┤
      │ uk_UA.UTF-8 │
      └─────────────┘
      (1 row)
      
      ┌─────────────────┐
      │        x        │
      ├─────────────────┤
      │  123 456,78грн. │
      │    7 654,30грн. │
      └─────────────────┘
      (2 rows)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-03-09
        • 1970-01-01
        • 2022-06-15
        • 1970-01-01
        • 2016-12-24
        • 2013-08-13
        • 2015-04-05
        相关资源
        最近更新 更多