【问题标题】:How to use SQL AUTOINCREMENT primary keys without excessive round trips to the database?如何在没有过多往返数据库的情况下使用 SQL AUTOINCREMENT 主键?
【发布时间】:2012-07-27 12:54:56
【问题描述】:

假设我有 1000 万个客户名称,每个名称都与一个或多个地址相关联,我想将数据放入 SQL 数据库中。

我制作了一个客户表和一个地址表。 Customer 表包含一个自动增量主键。每个 Address 条目都使用外键指向 Customer 表。

现在,对于 1000 万条记录中的每一条,我必须创建一条客户记录,将其插入到客户表中,然后再次检索它以获取分配给它的自动增量主键,以便在新的地址条目中使用。 2000 万次往返。呃。

除了使用 GUID 之外,还有更好的方法吗?

(我碰巧正在使用带有 SQLAlchemy 的 SQLite)

【问题讨论】:

  • 这个客户和地址数据目前存储在什么格式或平台上?
  • 这是假设数据。我的真实用例是用户生成和模拟数据的复杂数据集。例如,没有简单的 CSV 文件可供提取。

标签: sql sqlite sqlalchemy auto-increment


【解决方案1】:

这不是使用 SA,而是一个示例来说明在 Sqlite3 中的两个表中插入行而不是往返。

创建一个连接两个表的视图,然后编写一个代替触发器来处理插入,然后插入到您的视图中。如果您想更进一步,修改 SA 以插入到您的视图中应该不难。

create table customer (id integer primary key autoincrement, name text not null);

create table address (id integer primary key autoincrement, customer_id integer references customer not null, street text);

create view customer_view as 
select customer.id as customer_id, customer.name, address.id as address_id, address.street
from customer 
inner join address on customer.id = address.id;

create trigger customer_view_insert_trg
instead of insert on customer_view begin
insert into customer (name) values (new.name);
insert into address (customer_id, street) values ((select last_insert_rowid()), new.street);
end;

insert into customer_view (name, street) values ('Joe', 'Main Street');
insert into customer_view (name, street) values ('Bill', 'Water Street');

sqlite> select * from customer;
1|Joe
2|Bill

sqlite> select * from address;
1|1|Main Street
2|2|Water Street

【讨论】:

  • 没怎么用过触发器,但它们看起来很有趣。
【解决方案2】:

如果您将 Customer 和 Address 对象配置为在它们之间建立关系,则可以简单地插入它们而无需担心 ID。 SA 知道它们是相关的,并为您制定 FK 详细信息。

细心的读者会注意到 SA 对象关系教程addresses this use case directly by creating a User object with a related Address。我认识到 OP 的实际问题更复杂,但 ORM 教程似乎是一个很好的起点。 SA 是一种摇滚工具。

请注意,您仍然需要遍历 10M 原始数据对象,这可能意味着您的数据库的批量加载工具可能是更有效的解决方案,但这是一种不同的方法。

【讨论】:

  • 这似乎是 SQLAlchemy 的最佳解决方案。让 SA 整理外键比在每个客户插入并明确获取 id 后尝试提交或刷新快 10 倍。然而,即便如此,SQLAlchemy 仍然比使用 python sqlite3 接口直接使用 lastrowid 作为外键进行单独插入要慢 25 倍
  • 有兴趣的,或者推荐改进的,我的测试代码和时序结果贴在http://pastebin.com/zCmzDraU
【解决方案3】:

我认为应该这样做:http://www.sqlite.org/c3ref/last_insert_rowid.html

此例程从第一个参数中的数据库连接返回最近成功 INSERT 到数据库中的 rowid。

如果表有一个 INTEGER PRIMARY KEY 类型的列,那么该列是 rowid 的另一个别名。

虽然这是一个 C/C++ 接口函数,但该函数也作为 SQLite 核心函数存在(实际上,它只是 C/C++ 的一个包装器。http://www.sqlite.org/lang_corefunc.html

【讨论】:

  • 因为可能有其他客户端同时修改表,所以无法预测自增值。
  • 那么,您还有其他可以唯一标识数据的字段吗?我正在研究在 sqlite 中一次插入多个表的可能性,但不幸的是,大多数似乎讨论过这个问题的网站都被我所在的网络屏蔽了......
  • @braddock:哦等等,我们开始吧。找到了应该可以工作的东西。
  • 调用“Last Insert Rowid”仍然是数据库的往返。理想情况下,我想在一个 SQL 调用中插入我的所有客户。
  • 上次我检查过,SQLite 无论如何都不允许您在单个 SQL 语句中进行复合/多次插入,因此无论如何您都会进行多次 SQL 调用。不幸的是,我认为 SQLite 也不允许存储变量,因此您可以在您刚插入的客户地址的第一个插入语句中使用 last_insert_rowid() 作为外键的值,然后使用 select <foreign key> from <your address table> where rowid=last_insert_rowid() 作为以下内容地址。不涉及“往返”,因为数据全部由 SQL 服务器处理。
猜你喜欢
  • 2011-11-03
  • 2019-03-03
  • 1970-01-01
  • 2012-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多