我需要将名称和几何数据从 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
我的问题是:
directory || '/' || file_name
,以便从该文件复制数据,然后复制后续文件?geometry
之后的 ,
键,然后将其插入到 location_point GEOGRAPHY(Point, 4326)
列中。我怎样才能做到这一点?我被困在这里,任何帮助将不胜感激。谢谢!
现在我正在运行 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
除非您使用正确的字符串引用,否则它会在找到逗号的地方分割输入行,因此在您的情况下,它将把最后一个字段分成多个部分。要解决此问题,您可以
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
字段结构在整个文件中不是恒定的,这可能会被破坏。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;