从 PostgreSQL 中的 CSV 文件复制 postgis 数据

问题描述 投票:0回答:1

我需要将名称和几何数据从 CSV 文件导入到 PostgreSQL。

individuals.csv
文件格式如下:

first_name,last_name,location
Russell,Wolff,{"type":"Feature","geometry":{"type":"Point","coordinates":[73.0786759127481,29.730356801638685]},"properties":{}}

CSV 文件中的所有几何类型均为

"type":"Point"
,该文件约有 500K+ 条记录。以上只是 1 条记录的示例。

我创建了下表来存储数据:

CREATE TABLE individuals (
  id SERIAL PRIMARY KEY,
  first_name VARCHAR(255),
  last_name VARCHAR(255),
  location_point GEOGRAPHY(Point, 4326) -- Use GEOGRAPHY data type for storing points
);

我在下面编写了

plpgsql
函数来执行相同的操作:

CREATE OR REPLACE FUNCTION insert_csv_data(directory text) RETURNS void AS $$
DECLARE
    file_name text;
    csv_data json;
begin
    -- Loop through GeoJSON files in the specified directory
    FOR file_name IN 
        SELECT *
        FROM pg_ls_dir(directory)
    loop
        IF file_name LIKE '%.csv' then

        RAISE NOTICE 'GeoJSON data: %', pg_read_file('path/to/individuals.csv');
        
        COPY individuals(first_name, last_name, location_point)
        FROM 'path/to/individuals.csv'
        CSV HEADER;
        END IF;
    END LOOP;
    RETURN;
END;
$$ LANGUAGE plpgsql;

所以我的想法是,我将

directory
传递给包含多个 CSV 文件的函数,然后
COPY
将每个文件中的数据传递给表 individual。所以我在每个
file_name
上运行一个循环。

现在当我运行这个函数时:

SELECT public.insert_csv_data('path/to/directory');

我有一个

RAISE NOTICE 'GeoJSON data: %', pg_read_file('path/to/individuals.csv');
这是 individual.csv 文件的路径,它可以正确打印数据,如下所示:

GeoJSON data: first_name,last_name,location
Russell,Wolff,{"type":"Feature","geometry":{"type":"Point","coordinates":[73.0786759127481,29.730356801638685]},"properties":{}}

我收到错误

SQL Error [22P04]: ERROR: extra data after last expected column
  Where: COPY individuals, line 2: "Russell,Wolff,{"type":"Feature","geometry":{"type":"Point","coordinates":[73.0786759127481,29.730356..."
SQL statement "COPY individuals(first_name, last_name, location_point)
        FROM 'path/to/individuals.csv'
        CSV HEADER"
PL/pgSQL function insert_csv_data(text) line 15 at SQL statement

我的问题是:

  1. 现在我正在运行 COPY 命令,其中 FROM 作为 1 个文件的硬编码路径。如何动态替换每个 file_name 的路径,例如 COPY FROM 中的
    directory || '/' || file_name
    ,以便从该文件复制数据,然后复制后续文件?
  2. 我需要从 CSV 中提取
    geometry
    之后的
    ,
    键,然后将其插入到
    location_point GEOGRAPHY(Point, 4326)
    列中。我怎样才能做到这一点?

我被困在这里,任何帮助将不胜感激。谢谢!

postgresql csv postgis
1个回答
0
投票

现在我正在运行 COPY 命令,其中 FROM 作为 1 个文件的硬编码路径。如何动态替换每个 file_name 的路径,例如 COPY FROM 中的

directory || '/' || file_name
,以便从该文件复制数据,然后复制后续文件?

您可以在 PL/pgSQL 块中使用 动态 SQL

do $f$
declare 
    v_file text:=concat('/tmp/','individuals.csv');
begin 
    execute format('copy test from %1$L', v_file);
end $f$;

我需要从 CSV 中提取 , 之后的几何键,然后将其插入到列 location_point GEOGRAPHY(Point, 4326) 中。我怎样才能做到这一点?

由于文件的结构,

copy
命令:

COPY individuals(first_name, last_name, location_point) 
FROM 'path/to/individuals.csv' CSV HEADER;

相当于这种

insert
语句:

INSERT INTO individuals(
    first_name, 
    last_name, 
    location_point )
VALUES (
    'Russell',
    'Wolff',
    '{"type":"Feature"',
    '"geometry":{"type":"Point"',
    '"coordinates":[73.0786759127481',
    '29.730356801638685]}',
    '"properties":{}}' );

这会抛出错误;值比列多,因为

copy
默认为
delimiter ','

DELIMITER
指定分隔文件每行(行)内的列的字符。 文本格式默认为制表符,
CSV
格式默认为逗号。
必须是单个单字节字符。

除非您使用正确的字符串引用,否则它会在找到逗号的地方分割输入行,因此在您的情况下,它将把最后一个字段分成多个部分。要解决此问题,您可以

  1. 通过中间表运行数据,该中间表接受分为五个部分的
    location
    ,然后使用
    concat_ws(',',...)
    重新组装它。使用
    quote '~'
    (或文件中未使用的任何字符)覆盖默认
    quote '"'
    - 否则
    copy
    将解释并删除最后一列中的所有双引号
    "
    demo1
    CREATE TABLE individuals_raw(
        first_name text, 
        last_name text, 
        location_point_part1 text, 
        location_point_part2 text, 
        location_point_part3 text, 
        location_point_part4 text, 
        location_point_part5 text);
    
    COPY individuals_raw 
    FROM '/tmp/individuals.csv' CSV HEADER QUOTE '~';
    
    INSERT INTO individuals (first_name,last_name,location_point)
    SELECT first_name, 
           last_name, 
           ST_MakePoint(
               location_jsonb['geometry']['coordinates'][0]::float
              ,location_jsonb['geometry']['coordinates'][1]::float ) as location_point
    FROM (SELECT first_name, 
                 last_name, 
                 concat_ws(',',location_point_part1,
                               location_point_part2,
                               location_point_part3,
                               location_point_part4,
                               location_point_part5)::jsonb as location_jsonb
          FROM individuals_raw) a;
    
    如果您的
    location_point
    字段结构在整个文件中不是恒定的,这可能会被破坏。
  2. 更强大:用单引号将
    location_point
    字段括起来,并将
    quote $$'$$
    quote ''''
    参数添加到
    copy
    命令中。将逗号
    ,
    分隔符替换为分号
    ;
    并添加
    delimiter ';'
    参数:demo2
    CREATE TABLE individuals_raw(
      first_name text, 
      last_name text, 
      location_point jsonb);
    
    COPY individuals_raw(first_name, last_name, location_point) 
    FROM 'path/to/individuals.csv' CSV HEADER DELIMITER ';' QUOTE '''';
    
    INSERT INTO individuals (first_name,last_name,location_point)
    SELECT first_name, 
           last_name, 
           ST_MakePoint(
               location_point['geometry']['coordinates'][0]::float
              ,location_point['geometry']['coordinates'][1]::float ) as location_point
    FROM individuals_raw;
    
© www.soinside.com 2019 - 2024. All rights reserved.