【问题标题】:Load CSV to multiple tables solving key constraint issues将 CSV 加载到多个表以解决关键约束问题
【发布时间】:2018-09-05 08:35:47
【问题描述】:

在 MySQL 中,是否可以将 CSV 加载到存储过程中,该存储过程又将填充多个表,每个表都有外键依赖关系 Exl - table1 的主键用于 table2,而 table2 的主键是在table3中使用?

示例 - 在存储过程中要读取的具有 5 列的单个文件, CSV文件中的每一行/记录,使用column1和column2插入table1,得到主键,然后在表2中插入column 3和column4,得到主键,使用table1的主键和table2的主键和coulmn5插入到table3中

我正在考虑以下选项 -

  1. 有一个外部应用程序读取 CSV,并通过 数据 [CSV 中的每一行] 到带有准备语句的存储过程,这将需要 照顾后续插入所需的表。
  2. 加载 CSV 进入一个临时表,并有一个准备好的存储过程 语句,它将处理后续插入 所需的表。
  3. 有一个读取 CSV 的外部应用程序, 并有一组准备好的语句插入到后续 表。但是,从外部获取主键然后重新使用 会造成延误..

重新表述问题以了解解决此问题的性能有效方法,如果可以加载 CSV,我们该怎么做?

【问题讨论】:

  • 您的意思是对每个表执行多次 LOAD DATA INFILE 吗?当表数据存储在不同的文件中时。
  • 否,存储过程中要读取的单个文件有5列,对于csv文件中的每一行/记录,使用column1和column2插入到table1中,获取主键,然后在表2中插入第3列和第4列,获取主键并使用table1的主键和table2的主键和coulmn5插入到table3中
  • 这不是一件小事。有一个想法,但我应该尝试一下;)
  • 你能显示你的 CSV,例如一些行吗?是否包含 ID 值?
  • 是的。现在,我现在计划强制使用 ID 的 [主键] 而不是 AUTO_INCREMENT,否则速度会大大降低。例如线 - 1000000024,PDT2342,35,1424838470000,1577750400000,45,SK2342_543,1,真,0,USR23 1000000025,PDT1342,35,1424838470000,1577750400000,45,SK1342_54,1,真,0,USR23 跨度>

标签: mysql stored-procedures


【解决方案1】:

是的 - 可以做到。我不相信应该这样做,因为它将大量业务逻辑放入数据库中,这通常被认为是一件坏事。这在数据库之外的应用程序中更容易更好地完成。

但是 - 问题是可以做到吗。我会通过将 CSV 加载到临时表中来做到这一点(因为它看起来不像有一种方法可以在存储过程中逐行读取)。然后您可以遍历表中的所有行并执行您需要执行的操作。

希望能给你一个让你前进的线索。

【讨论】:

  • 好的,谢谢。采用了加载到临时表的方法,但是,我们想考虑通过外部应用程序加载与发出插入语句的存储过程所需的时间。正在讨论的数据量约为 20-25 百万条记录作为源 csv。您能否建议一种高效的方法?
  • 我会在外部应用程序中执行它并使用准备好的语句 - 这意味着 MySql 只需要优化一次查询 - 然后您可以使用它数百万次。
  • 我正在考虑以下选项 - 1. 有一个读取 CSV 的外部应用程序,并将数据传递到带有准备语句的存储过程,该过程将负责后续插入所需的表。 2. 将 CSV 加载到临时表中,并有一个带有准备语句的存储过程,该过程将负责后续插入所需表的操作。 3. 有一个读取 CSV 的外部应用程序,并有一组准备好的语句插入到后续表中。但是,从外部获取主键然后重新使用会导致延迟..
  • 应用程序和存储过程将以相同的方式获取最后一个插入 ID - 从应用程序而不是在存储过程中执行此操作的任何延迟都是微不足道的 - 在任何好的 mysql 客户端库中将成为结果对象的一部分。您还应该构建应用程序,以最大限度地提高在需要准备新语句之前使用准备好的语句的次数。
  • 你建议#1?我倾向于#2,减少从外部应用程序到mysql的跃点
【解决方案2】:

不,请参阅Appendix C1 - Restrictions on Stored Programs,它告诉您 LOAD DATA 不能在存储的例程中使用。

存储例程中不允许使用 SQL 语句

存储的例程不能包含任意 SQL 语句。以下 不允许声明:

锁定语句 LOCK TABLES 和 UNLOCK TABLES。

改变视图。

加载数据和加载表。

同样,在documentation for prepared statements 中,您会发现它显示以下内容

一般情况下,SQL 准备语句中不允许的语句是 也不允许在存储的程序中。

然后给出一个有用的允许语句列表。不幸的是,您不会在存储程序的文档中找到相同的列表(它可能更有用)。

【讨论】:

    【解决方案3】:

    有一个解决方案。您可以使用一些辅助触发器。假设您将数据(或从文件加载)插入到 ma​​in 表中。然后你有 table1(c1, c2)table2(c3,c4)table3(c5),就像你的问题一样。 p>

    现在:

    1. 我们在 ma​​in 表上使用 trigger1 将数据插入 table1。一些会话变量被用于存储临时字段值。
    2. 我们在 table1 表上使用 trigger2 将数据插入到 table2
    3. 我们在 table2 表上使用 trigger3 将数据插入到 table3

    让我们看一下脚本:

    CREATE TABLE main (
      c1 varchar(255) DEFAULT NULL,
      c2 varchar(255) DEFAULT NULL,
      c3 varchar(255) DEFAULT NULL,
      c4 varchar(255) DEFAULT NULL,
      c5 varchar(255) DEFAULT NULL
    )
    ENGINE = INNODB;
    
    CREATE TABLE table1 (
      id int(11) NOT NULL AUTO_INCREMENT,
      c1 varchar(255) DEFAULT NULL,
      c2 varchar(255) DEFAULT NULL,
      PRIMARY KEY (id)
    )
    ENGINE = INNODB,
    AUTO_INCREMENT = 10;
    
    CREATE TABLE table2 (
      id int(11) NOT NULL AUTO_INCREMENT,
      id_ref_table1 int(11) DEFAULT NULL,
      c3 varchar(255) DEFAULT NULL,
      c4 varchar(255) DEFAULT NULL,
      PRIMARY KEY (id)
    )
    ENGINE = INNODB
    AUTO_INCREMENT = 20;
    
    CREATE TABLE table3 (
      id int(11) NOT NULL AUTO_INCREMENT,
      id_ref_table2 int(11) DEFAULT NULL,
      c5 varchar(255) DEFAULT NULL,
      PRIMARY KEY (id)
    )
    ENGINE = INNODB;
    
    CREATE TABLE main (
      c1 varchar(255) DEFAULT NULL,
      c2 varchar(255) DEFAULT NULL,
      c3 varchar(255) DEFAULT NULL,
      c4 varchar(255) DEFAULT NULL,
      c5 varchar(255) DEFAULT NULL
    )
    ENGINE = INNODB,
    AUTO_INCREMENT = 30;
    
    DELIMITER $$
    
    CREATE TRIGGER trigger1
    AFTER INSERT
    ON main
    FOR EACH ROW
    BEGIN
      SET @c3 = NEW.c3;
      SET @c4 = NEW.c4;
      SET @c5 = NEW.c5;
    
      INSERT INTO table1 (c1, c2)
        VALUES (NEW.c1, NEW.c2);
    END
    $$
    
    CREATE TRIGGER trigger2
    AFTER INSERT
    ON table1
    FOR EACH ROW
    BEGIN
      INSERT INTO table2 (id_ref_table1, c3, c4)
        VALUES (NEW.id, @c3, @c4);
    END
    $$
    
    CREATE TRIGGER trigger3
    AFTER INSERT
    ON table2
    FOR EACH ROW
    BEGIN
      INSERT INTO table3 (id_ref_table2, c5)
        VALUES (NEW.id, @c5);
    END
    $$
    
    DELIMITER ;
    

    还有一些带有结果的 INSERT 语句:

    INSERT INTO main(c1, c2, c3, c4, c5) VALUES
    ('1', '2', '3', '4', '5'),
    ('11', '22', '33', '44', '55');
    
    
    SELECT id, c1, c2 FROM table1;
    ------------------------------
    10  1   2
    11  11  22
    
    SELECT id, id_ref_table1, c3, c4 FROM table2;
    ---------------------------------------------
    20  10  3   4
    21  11  33  44
    
    SELECT id, id_ref_table2, c5 FROM table3;
    -----------------------------------------
    30  20  5
    31  21  55
    

    【讨论】:

    • 是的,我试过了,但是 LOAD DATA INFILE 比这种方法更快
    • 当然简单的 LOAD DATA 很快。我们只是试图以最低要求制作一个 SQL 解决方案。您应该将 INSERT INTO main... 更改为 LOAD DATA...INTO TABLE main... 语句。
    【解决方案4】:

    经过如上所述的多种方法后,找到了最佳方法。将整体任务分为两个主要部分 - 拆分数据,加载表格

    • 拆分 - 将源文件拆分为多个 CSV 的外部应用程序 [匹配 table1、table2 和 table3 的架构]
    • 加载 - 使用 LOAD DATA INFILE 这是填充表格的最快方式。

    [对于所有这些表,硬编码主键而不是依赖于 AUTO_INCREMENT]

    感谢大家的意见、建议和指导。

    【讨论】:

      猜你喜欢
      • 2021-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-10
      • 1970-01-01
      • 2017-04-13
      • 1970-01-01
      相关资源
      最近更新 更多