【问题标题】:"Insert...select" transactions with multiple values and normalized database具有多个值和规范化数据库的“插入...选择”事务
【发布时间】:2023-03-18 23:54:01
【问题描述】:

This question 正确解释了如何同时在数据库中插入数据以实现“分层”结构(is-a 关系):

不,您不能在一个 MySQL 命令中插入多个表。你 但是可以使用事务。

BEGIN;
INSERT INTO base_class (data0, data1) VALUES('0', '1');
INSERT INTO derived_class (base_id, data2, data3) VALUES(LAST_INSERT_ID(), '2', '3');
COMMIT;

该解决方案在一次插入一个东西时效果很好。

但是,我需要使用 INSERT...SELECT 同时插入多个值。

BEGIN;
INSERT INTO base_class (data0, data1) SELECT d0, d1 FROM stuff
INSERT INTO derived_class (base_ids, data2, data3) SELECT ???, d2, d3 FROM stuff
COMMIT;

我应该如何告诉 MySQL 通过基类 ID 将 derived_class 的每个实例“链接”到先前创建的 base_class

理想情况下,我会同时“循环”两个表,但这在 MySQL 或 MariaDB 中是不可能的:

# PSEUDOCODE! NOT VALID.
BEGIN;
INSERT INTO 
                base_class (data0, data1) 
     ALONG_WITH derived_class (base_ids, data2, data3)   
     SELECT d0, d1, GET_ALONG_FIELD(base_class, id), d2, d3 FROM stuff
     #      _______ table 1 values
     #              _______________________________________ table 2 values
COMMIT;

如何在保持表格的“分层”设计的同时解决此问题?


编辑:

我再次提出这个问题是因为我很好奇是否有可能实现我想要的行为而不必求助于类似过程的 SQL 代码(使用游标)。

有没有办法在不使用游标的情况下解决这个问题?

【问题讨论】:

  • 使用 MySQL 外部的脚本语言可能会容易得多。 Python、Ruby 甚至 NodeJS 都可以轻松处理这种递归。
  • @tadman:没错,最简单的解决方案是使用外部语言。但是,我真的很好奇这是否可以在纯 MySQL 中实现

标签: mysql sql database normalization mariadb


【解决方案1】:

我不完全确定我理解你的问题。我认为您正在尝试以相同的顺序将d0d1 插入base_classbase_idsd2d3derived_class 中,以便@987654328 中的第n 个插入值@ 和derived_class 都来自stuff 中的第n 条记录。如果我的理解是正确的,下面的交易会做你想做的。

BEGIN;
INSERT INTO base_class (data0, data1)
SELECT d0, d1
FROM stuff
ORDER BY base_ids
INSERT INTO derived_class (base_ids, data2, data3)
SELECT base_ids, d2, d3
FROM stuff
ORDER BY base_ids
COMMIT;

【讨论】:

  • base_ids 不是stuff 表中的列。对不起,如果我的解释令人困惑。我想说我有一个对象层次结构(用 OOP 术语思考)-derived_class 行是派生自base_class 行的实例。如果我需要创建derived_class 的实例,我会被外键约束强制先创建base_class 实例,然后将其ID 存储在随后创建的derived_class 实例中。当我必须创建一个时很容易,因为我可以使用LAST_INSERT_ID()。如果我必须创建更多实例,我将无法获得所有新 ID。
【解决方案2】:

光标应该可以解决问题。您可以循环遍历stuff 表并一次执行一个插入,同时获取插入ID。像这样的东西(未经测试):

BEGIN;

  DECLARE get_stuff CURSOR FOR SELECT id FROM stuff;
  DECLARE current_id INT;

  OPEN get_stuff;
  insert_stuff: LOOP

    FETCH get_stuff INTO current_id;
    INSERT INTO base_class (data0, data1)
      SELECT d0, d1 FROM stuff WHERE id = current_id
    INSERT INTO derived_class (base_ids, data2, data3)
      SELECT mysql_insert_id(), d2, d3 FROM stuff WHERE id = current_id

  END LOOP insert_stuff;
  CLOSE get_stuff;

COMMIT;

另一种可能的方法是在两个“插入”表上创建一个视图。根据表的结构,插入视图可能会受到限制,但类似这样的方法可能有效(也未经测试):

CREATE VIEW stuff_view AS
  SELECT b.data0, b.data1, d.data2, d.data3 FROM base_class AS b
  INNER JOIN dervied_class AS d ON (d.base_id = b.id)

INSERT INTO stuff_view (data0, data1, data2, data3)
  SELECT d0, d1, d2, d3 FROM stuff

我实际上不确定 MySQL 是否会自动为基类表和派生类表分配适当的 ID。

【讨论】:

  • 游标似乎确实是解决这个问题的唯一方法a。很酷的功能
  • 我再次打开了这个问题,因为我真的很好奇这是否可以在没有过程编程和游标的情况下解决。
  • @VittorioRomeo:你试过view吗?我知道插入视图有限制,但可能值得一试。看看我更新的答案。
猜你喜欢
  • 2011-07-07
  • 1970-01-01
  • 2015-11-13
  • 2011-06-09
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 2017-02-02
相关资源
最近更新 更多