如何在不进行过多重复检查的情况下将值插入高度规范化的数据库中?

Joe*_*Joe 6 mysql mariadb

情况:

我们项目的负责人决定使用高度规范化的数据库作为我们的数据库设计。这意味着大表的每个字段实际上都是一个 ID 而不是实际值。他的意图是没有任何类型的重复,即使在重复不会伤害的地方(人们的名字,那种东西)。

但这确实会导致一个问题:插入新数据时,我们需要检查每个子表以查看该值是否存在(第一次查询),如果不存在则插入它(第二次查询),否则检索 ID,执行这实际上是主表中的每一列(所以 30 次左右),然后我们可以创建我们真正想要获得的对象。(创建一个对象大约需要 60 次数据库命中!)。

我们在spring工作,所以我们使用jdbcTemplate来实际建立数据库连接,每次查询都是昂贵的。当我们插入或更新数以千计的新记录时,这实际上会严重减慢数据库的速度。

这整个过程让我觉得很脏很不对,所以我想问:有没有更好的方法?如果子查询不存在,是否可以插入一个值,如果存在则不插入,并在这两种情况下返回实际键,该键立即用于在主表中设置 ID?有没有一种优雅的解决方案来减少查询数量而不引入太多复杂的 SQL(为了团队成员)?

Aki*_*ina 3

有点想...

形式上你有类似于这个简化结构的东西:

CREATE TABLE slave1 (id PK, value UNIQUE);
CREATE TABLE slave2 (id PK, value UNIQUE);
CREATE TABLE main (id PK, id_slave1 FK, id_slave2 FK);
Run Code Online (Sandbox Code Playgroud)

当需要插入2条记录(id_1, val_1_1, val_1_2)和(id_2, val_2_1, val_2_2)时,执行:

CREATE TEMPORARY TABLE temp (val_slave1, val_slave2) [ENGINE=Memory];

INSERT INTO temp (val_slave1, val_slave2)
VALUES (val_1_1, val_1_2),
       (val_2_1, val_2_2);

INSERT IGNORE INTO slave1 (value)
SELECT DISTINCT val_slave1
FROM temp;

INSERT IGNORE INTO slave2 (value)
SELECT DISTINCT val_slave2
FROM temp;

INSERT INTO main (id_slave1, id_slave2)
SELECT slave1.id, slave2.id
FROM temp
JOIN slave1 ON temp.val_slave1 = slave1.value
JOIN slave2 ON temp.val_slave2 = slave2.value;
Run Code Online (Sandbox Code Playgroud)

temp当插入的值量较少时,引擎可能是Memory,如果插入的数据数组很大,则可能是InnoDB或其他引擎。

INSERT IGNORE 在 UNIQUE 索引字段上运行得足够快。它保证从表中没有重复项,并且在插入主表时必须插入的值将存在于从表中。

最终查询也必须很快 - 特别是当临时表字段也被索引时。

如果您只需要插入一条记录,那么您当然可以不使用表temp...但我认为统一比稍微简化更安全。

当然,这可能是优化的。例如,所有插入可能会加入到一个存储过程中,并且您不需要在“60 个数据库命中”中,一次 CALL 就足够了。最后,您必须仅执行 3 个与要插入的记录数无关的查询。并且其中只有一个(插入temptable)可能会很大(甚至可以分为很多插入)。