将所有重复引用更改为对 MAX(id) 的引用

Change all dup-references to references to MAX(id)

这是一个关于sqlite的问题。

customers (id INTEGER PRIMARY KEY, name TEXT)
orders (id INTEGER PRIMARY KEY, cid INTEGER)

我想从 customers 中删除所有重复条目(保留 MAX(id) 条目)。但在此之前,我想将 orders 中的所有引用更改为相应的 MAX(id) 值。

这是我认为正确的做法:

UPDATE orders
SET cid = (SELECT  MAX(c.id)
           FROM customers c
           JOIN (SELECT  name, id
                 FROM  customers
                 WHERE id = orders.cid) q
           ON c.name = q.name
           GROUP BY c.name);

DELETE FROM customers
WHERE id NOT IN (SELECT MAX(id)
                 FROM customers
                 GROUP BY name);

但尤其是第一个查询已经非常慢,已经有大约 10,000 个客户。有更快的方法吗?

一些数字:我们有 120,000 名客户,其中 appx。 30,000 有 COUNT(*) > 1(当 GROUP BY 名称时)。除此之外,我们还有 200,000 个订单。完成上述查询需要将近20分钟。

使用临时 table 的 ID 可能会获得更好的性能。据我了解,SQLite 在处理此查询时遇到问题,因为在每次更新和删除时,客户都会发生变化。请注意,这种方法最适合事务。

BEGIN Transaction;

创建 Temp Table TempCustomers 作为 SELECT id,MAX(id) 作为 MaxId 来自客户 按名称分组;

CREATE TEMP TABLE TempCustomers AS 
    SELECT k.id, q.MaxId 
    FROM customers k JOIN 
       (SELECT MAX(d.id) as MaxId, d.name FROM customers d GROUP BY d.name)
    q ON q.name = k.name;

UPDATE orders
    SET cid = (SELECT MaxId
       FROM TempCustomers c
       WHERE id = orders.cid);

DELETE FROM customers
    WHERE id NOT IN (SELECT MaxId
             FROM TempCustomers);

COMMIT;

当您断开连接时,您的 Temp table 将从内存中删除。或者,如果你想保持连接而不占用内存,你可以使用 DROP Temp Table

编辑:在评论中提出的最终方法。

首先,为orders.cid添加一个索引。然后使用主键创建临时 table,并将 id 交换插入其中(而不是即时创建)。最后,执行清理。

BEGIN Transaction;

CREATE TEMP TABLE TempCustomers
   (Id Integer PRIMARY KEY,
    MaxId Integer);

INSERT INTO TempCustomers SELECT k.id, q.MaxId 
    FROM customers k JOIN 
       (SELECT MAX(d.id) as MaxId, d.name FROM customers d GROUP BY d.name)
    q ON q.name = k.name;

UPDATE orders
    SET cid = (SELECT MaxId
       FROM TempCustomers c
       WHERE id = orders.cid);

DELETE FROM customers
    WHERE id NOT IN (SELECT MaxId
             FROM TempCustomers);

DROP TABLE TempCustomers;

COMMIT;