【问题标题】:How to import "a lot" of data to MySQL with PHP and foreign keys?如何使用 PHP 和外键将“大量”数据导入 MySQL?
【发布时间】:2013-07-21 12:39:10
【问题描述】:

我有这些表:

create table person (
    person_id int unsigned auto_increment, 
    person_key varchar(40) not null, 
    primary key (person_id), 
    constraint uc_person_key unique (person_key)
) 
-- person_key is a varchar(40) that identifies an individual, unique 
-- person in the initial data that is imported from a CSV file to this table

create table marathon (
    marathon_id int unsigned auto_increment,  
    marathon_name varchar(60) not null, 
    primary key (marathon_id) 
)

create table person_marathon (
    person_marathon _id int unsigned auto_increment,  

    person_id int unsigned, 
    marathon_id int unsigned,

    primary key (person_marathon_id),
    foreign key person_id references person (person_id), 
    foreign key marathon_id references person (marathon_id),

    constraint uc_marathon_person unique (person_id, marathon_id)  
)

Person 表由包含大约 130,000 行的 CSV 填充。此 CSV 包含每个人和一些其他人员数据的唯一 varchar(40)。 CSV 中没有 ID。

对于每场马拉松,我都会得到一个 CSV,其中包含 1k - 30k 人的列表。 CSV 基本上只包含person_key 值的列表,这些值显示了哪些人参加了特定的马拉松比赛。

将数据导入person_marathon表以保持FK关系最好的方法是什么?

这些是我目前能想到的想法:

  • person_id + person_key信息从MySQL中取出,并在PHP中合并person_marathon数据,得到person_id,然后插入person_marathon表中

  • 使用临时表进行插入...但这是为了工作,我被要求永远不要在此特定数据库中使用临时表

  • 根本不要使用person_id,只使用person_key 字段,但我必须加入varchar(40),这通常不是一件好事

  • 或者,对于插入,让它看起来像这样(我必须插入 <hr> 否则它不会将整个插入格式化为代码):

    insert  into person_marathon 
    
    select  p.person_id, m.marathon_id
    
    from    ( select 'person_a' as p_name, 'marathon_a' as m_name union 
              select 'person_b' as p_name, 'marathon_a' as m_name ) 
              as imported_marathon_person_list 
    
            join person p 
               on p.person_name = imported_marathon_person_list.p_name
    
            join marathon m 
               on m.marathon_name = imported_marathon_person_list.m_name
    

    该插入的问题在于,要在 PHP 中构建它,imported_marathon_person_list 会很大,因为它很容易成为 30,000 个select union 项。不过,我不知道该怎么做。

【问题讨论】:

  • 您是否为此研究过 ETL 流程? Pentaho PDI 可能是?
  • 我希望能够用 PHP 编写一些东西。我只是不确定构造插入的最佳方法。我想我倾向于拉出person_id + person_key,然后在 MySQL 插入之前将其合并到 PHP 中。这是一个非常小的项目,我不确定我们是否需要一个新工具来处理它。
  • 你应该试试 Pentaho。我已经用语言(php、java)做了很多大型导入(几个 GB),并且我尝试过 Pentaho。只需尝试 30 分钟,然后告诉我们。 ;)
  • 我会选择选项 2 或 3。一个 40 字节的字符串,如果索引正确,对于连接不会 可怕 - 特别是如果您的大多数查询是无论如何都要对该字符串执行查找。加载到临时文件中是一个不错的选择:到底为什么你被要求永远不要使用它们(它们不会在你的数据库连接结束后持续存在,所以谁在乎呢?)
  • @eggyal 所以临时表?如果这是最好的选择,我可以为此辩护。我只是不确定是否还有其他事情。 Pentaho 甚至可以自动化吗?临时表肯定比新工具更容易争论。

标签: php mysql insert large-data


【解决方案1】:

我处理过类似的数据转换问题,但规模较小。如果我正确理解了您的问题(我不确定),听起来使您的情况具有挑战性的细节是:您尝试在同一步骤中做两件事:

  • 将大量行从CSV导入mysql,然后
  • 进行转换,使人员-马拉松关联通过 person_id 和 marathon_id 工作,而不是(笨重且不受欢迎的)varchar personkey 列。

简而言之,我会尽一切可能避免在同一步骤中同时做这两件事。将其分为这两个步骤 - 首先以可容忍的形式导入 所有数据,然后再对其进行优化。 Mysql 是进行这种转换的好环境,因为当您将数据导入到 people 和 marathons 表中时,ID 会为您设置好。

第 1 步:导入数据

  • 我发现在 mysql 环境中执行数据转换比在它之外更容易。因此,将数据以一种保留人与马拉松关联的形式进入 mysql,即使它不是最佳的,也不必担心事后更改关联方法。
  • 您提到了临时表,但我认为您不需要任何临时表。在persons_marathons 表上设置一个临时列“personkey”。当您导入所有关联时,您暂时将 person_id 留空,只需导入 personkey。重要的是,确保 personkey 是关联表和 person 表上的索引列。然后就可以稍后过一遍,为每个personkey填写正确的person_id,不用担心mysql效率低下。
  • 我不清楚马拉松表数据的性质。你有成千上万的马拉松要参加吗?如果是这样,我不羡慕你每次马拉松处理 1 个电子表格的工作。但如果它更少,那么您也许可以手动设置马拉松表。让mysql为你生成马拉松ID。然后,当您为每个马拉松导入 person_marathon CSV 时,请务必在与该马拉松相关的每个关联中指定该马拉松 ID。

导入数据后,您将拥有三个表: * 人 - 你有丑陋的 personkey,以及新生成的 person_id,以及任何其他字段 * marathons - 此时你应该有一个 marathon_id,对吧?要么是新生成的,要么是您从某些旧系统中继承的数字。 * person_marathons - 这个表应该填写 marathon_id 并指向 marathons 表中的正确行,对吗?您还有 personkey(丑陋但存在)和 person_id(仍然为空)。

第二步:使用personkey为关联表中的每一行填写person_id

然后你要么直接使用Mysql,要么编写一个简单的PHP脚本,为persons_marathons表中的每一行填写person_id。如果我无法让 mysql 直接执行此操作,我通常会编写一个 php 脚本来一次处理一行。步骤很简单:

  1. 查找 person_id 为空但 personkey 不为空的任意 1 行
  2. 查找该 personkey 的 person_id
  3. 将 person_id 写入该行的关联表中

如果您一直遇到超时问题或类似问题,您可以告诉 PHP 重复 100 次然后结束脚本,或者 1000 次。

这种转换涉及大量查找,但每次查找只需要针对单行。这很吸引人,因为您在任何时候都不需要要求 mysql(或 PHP)“将整个数据集保持在头脑中”。

此时,您的关联表应该为每一行填写 person_id。现在可以安全地删除 personkey 列了,瞧,你有了高效的外键。

【讨论】:

  • “马拉松上的关联通过 person_id 和 marathon_id 工作,而不是(笨拙且不受欢迎的)varchar personkey 列”——这正是问题所在。每个月大约有 20-40 场马拉松比赛,但这些数据不是以 CSV 格式接收的……它只是附加到 person_marathon 数据上,因此与其他所有数据相比,它相当容易处理。
  • 自动化似乎有点困难,我担心在person_marathon 中添加新旧数据的额外信息,然后从表中删除(甚至是不必要的)数据。
  • 好的,感谢 cmets 并祝您好运,Gloomy。听起来您将继续以这种不需要的格式接收新的马拉松数据(其中 varchar personkey 是唯一的人参考)?我一直在回应,好像那是一次一次性的挑战。但即便如此,我认为我仍然会从考虑我提出的解决方案开始:设置persons_marathons 表以有一个额外的varchar 列,尽管听起来您最终无法删除该personkey 列,因为它将继续被需要。 Mysql 可以处理冗余。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-09
  • 1970-01-01
  • 1970-01-01
  • 2014-07-15
相关资源
最近更新 更多