【问题标题】:Most effective way how to avoid inserting duplicates into database?如何避免将重复项插入数据库的最有效方法?
【发布时间】:2014-01-22 20:03:13
【问题描述】:

我将首先描述我的情况,以便使以下问题尽可能清楚。

为简单起见,假设我在 MySQL 数据库 (InnoDB) 中有一个表,其中包含有关狗的记录,结构如下:

dog_id (PK) | dog_name

表中有 10,000,000 行(每行代表一条唯一的狗),并在 dog_name 列上建立索引。

我的程序搜索我需要处理的 vets 记录。每条记录都以某种方式与一只狗相关联,每条狗大约有 100 条记录。我想找到尚未插入数据库的狗。

这意味着连续 100 次正在处理的记录可能是关于已经在数据库中的狗,因此不必将狗添加到数据库中。但有时会发生(如前面提到的 1:100 比例)我需要将一只狗添加到数据库中,因为这是程序第一次处理关于狗的记录。 (我希望这个例子能说明我的情况)

我的问题是: 如何验证狗还没有被插入数据库最有效的方法是什么?

  1. 将所有狗的名字(假设世界上所有的狗都有唯一的名字)加载到程序(一个集合)的内存中,并检查狗是否在集合中。当它在集合中时,我跳过记录,当它不在时,我插入狗。
  2. 将列定义为 UNIQUE 并尝试插入所有记录。当因为唯一性而出现数据库错误时,我就跳过狗继续。
  3. 每次处理记录时查询数据库以查明狗是否在数据库中,如果在数据库中,则跳过该记录,如果不在,则将狗插入表中。

尽可能多地为您提供信息。我使用 Python、SqlAlchemy、MySQL、InnoDB。

【问题讨论】:

    标签: python mysql database performance


    【解决方案1】:

    你应该使用 dog_name 作为主键,然后使用

    INSERT INTO dogs (dog_name) VALUES ('[NAME HERE]') ON DUPLICATE KEY UPDATE dog_name='[NAME HERE]';
    

    这只会插入唯一的狗名。如果您仍想为每只狗使用数字 ID,您可以将该列设置为自动递增,但主键应该是狗名(假设所有狗名都是唯一的)。

    SQLAlchemy 没有内置此功能,but can make force it to make a similar query with session.merge()

    【讨论】:

    • 我以前不知道“ON DUPLICATE KEY”语法。谢谢。但是,我想“INSERT IGNORE INTO”在我的情况下会很有用。问题是,SELECT + INSERT 不会更快吗?考虑到 100:1 的比率(仅 SELECT 与 SELECT 后跟 INSERT)
    • 我不建议使用INSERT IGNORE,因为它会忽略查询中的任何错误,包括重复的键。执行SELECT,然后执行INSERT 会将一些Python 与SQL 混合,这未经过优化并且可能比仅执行INSERT...ON DUPLICATE KEY/INSERT IGNORE(纯SQL)要慢。所以我建议 INSERT...ON DUPLICATE KEY。
    【解决方案2】:

    类似选项 2 或选项 3 的方法效果最好;他们应该花费相似的时间,哪一个获胜将取决于 MySQL/InnoDB 如何确定发生冲突。我实际上不知道;使用 UNIQUE 键插入可能会触发与 SELECT 相同的操作。制作原型并分析性能。

    如果性能是一个问题,您总是可以手动编写 SELECT 语句,因为它相对简单。这减少了构建 SQL 的 Python MySQL 开销;这通常不是一个大问题,但 SQLAlchemy 可以添加数十层函数调用,以支持其构造任意查询的能力。您可以使用 Python 字符串格式将这些调用短路。

    假设 's' 是您的 SQLAlchemy Session 对象:

    def dog_in_db(dog_name):
        q = 'SELECT COUNT (*) FROM dogs WHERE dog_name = %s;' % dog_name
        res = s.execute(q)
        return res.first()[0] > 0
    

    您也可以尝试 SELECTing 并检查是否返回任何行:

        q 'SELECT dog_id FROM dogs WHERE dog_name = %s;' % dog_name
        res = s.execute(q)
        return res.rowcount() > 0
    

    假设您的选项 1 意味着从数据库中加载所有名称,它会很慢。 MySQL 将始终比 Python 更快地执行它支持的任何单个操作;并且您在这里所做的是完全相同的单个操作(在列表中查找成员)。

    【讨论】:

    • 谢谢。只有两件事:1)您能否更具体一点 - “硬编码 SELECT 语句”是什么意思? 2) 在进一步研究您提出的想法后,我发现性能最好的SELECT 将是SELECT COUNT(1) FROM dogs WHERE dog_name = %s LIMIT 1。你怎么看?
    • 我想我打错了:我的意思是“手工编码”。听起来你处理得很好。这听起来像是一段非常合理的 SQL。我猜LIMIT 1 位加速了 SQLAlchemy Result 解析。听起来COUNT(1)COUNT(*) 相同,至少对于 MySQL:stackoverflow.com/questions/1221559/count-vs-count1
    猜你喜欢
    • 2014-05-07
    • 2011-01-14
    • 2022-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多