使用外键导入 csv

Posted

技术标签:

【中文标题】使用外键导入 csv【英文标题】:Import a csv with foreignkeys 【发布时间】:2021-01-01 02:25:22 【问题描述】:

假设我有 2 个表:Student 和 Groups。

Group 表有 2 列:id、GroupName Student 表有 3 列:id、StudentName 和 GroupID GroupID 是组字段的外键。

我需要从 CSV 导入学生表,但在我的 CSV 中而不是组 ID 中显示组的名称。如何在不修改 csv 的情况下使用 pgAdmin 导入它?

【问题讨论】:

【参考方案1】:

@LaurenzAlbe 的建议是显而易见的方法(恕我直言,从不将电子表格直接加载到 你的桌子,它们是不值得信赖的野兽)。但我相信您在加载登台后的实施 表有缺陷。 首先,使用 row_number() 几乎可以确保您获得相同组名的重复 ID。 无论先前加载的组数如何,ID 始终从 1 到 1 递增到组名称的数量,并且您无法确保后续电子表格中的顺序相同。当您有一个以前不存在的组时会发生什么。 此外,没有验证组名不存在。结果:重复的组名和/或同名的多个 ID。 其次,您尝试使用电子表格中的 id 作为学生 (std) 表中充满错误可能性的 id。您如何确保该数字在电子表格中是唯一的? 即使在单个电子表格中是唯一的,您如何确保另一个电子表格不使用与前一个相同的数字。或者假设多个用户创建电子表格,其中一个用户的编号不会与另一位用户重叠,即使所有用户 用户非常清楚他们使用的数字。结果:重复的身份证号码。 更好的方法是在组表名列上放置一个唯一键,然后将阶段表中的任何组名插入到组中,以捕获任何重复名称错误(在冲突时使用)。然后直接从stage表加载student表 同时通过(现在唯一的)组名从组表中选择组 ID。

create table csv_load_temp( junk_num integer, student_name text, group_name text);

create table groups( grp_id integer generated always as identity
                  , name text
                  , grp_key text generated always as ( lower(name) ) stored
                  , constraint grp_pk 
                               primary key (grp_id)
                  , constraint grp_bk
                               unique (grp_key) 
                  ); 

create table students (std_id integer generated always as identity 
                    , name text 
                    , grp_id integer
                    , constraint std_pk 
                                 primary key (std_id)
                    , constraint std2grp_fk
                                 foreign key (grp_id)
                                 references groups(grp_id)
                );
                
-- Function to load Groups and Students
create or replace function establish_students() 
 returns void 
 language sql 
as $$
insert into groups (name) 
     select distinct group_name
       from csv_load_temp
         on conflict (grp_key) do nothing;
         
insert into students (name, grp_id)
     select student_name, grp_id 
       from csv_load_temp t 
   join groups grp
     on (grp.name = t.group_name);
$$; 

groups 表需要 Postgres v12。对于以前的版本,删除列 grp_key couumn 并将唯一约束直接放在名称列上。如何处理资本化取决于您的业务逻辑。 有关完整示例,请参见小提琴。显然,Establish_Students 函数中的 2 个插入可以独立运行。在这种情况下,函数本身就不需要了。

【讨论】:

【参考方案2】:

根据 Laurenz 的回答,使用以下脚本:

创建一个临时表以从 CSV 文件插入:

CREATE TEMP TABLE std_temp (id int, student_name char(25), group_name char(25));

然后,导入 CSV 文件:

COPY std_temp FROM '/home/username/Documents/std.csv' CSV HEADER;

现在,为学生和小组创建 stdgrp 表:

CREATE TABLE grp (id int, name char(25));
CREATE TABLE std (id int, name char(20), grp_id int);

轮到grp 表根据distinct 组名称的值填充。考虑row_number() is use to provide value for id`:

INSERT INTO grp (id, name) select row_number() OVER (), * from (select distinct group_name from std_temp) as foo;

最后一步,根据join选择数据,然后插入std表中:

insert into std (id, name, grp_id) select std_temp.id, std_temp.student_name,grp.id from std_temp inner join grp on std_temp.group_name = grp.name;

最后,从最终的std 表中检索数据:

select * from std;

【讨论】:

【参考方案3】:

您最简单的选择是将文件导入到像 CSV 文件一样定义的临时表中。然后您可以将该表与“groups”表连接起来,并使用INSERT INTO ... SELECT ... 填充“students”表。

当然还可以选择在两个表的连接上定义一个视图,并在视图上定义一个INSTEAD OF INSERT 触发器,将值插入到基础表中。然后您可以直接将数据加载到视图中。

【讨论】:

以上是关于使用外键导入 csv的主要内容,如果未能解决你的问题,请参考以下文章

问题:使用 phpMyAdmin Works 导入 csv,Php 脚本以不同方式处理 csv 导入

由于外键 phpMyAdmin 的约束,BigDump 无法导入

RestKit 使用外键从 JSON 导入

如何把csv文件导入到sql数据库

使用导入向导导入 CSV 时 MYSQL 崩溃

macmatlab导入大型csv