0

上下文:
我有一个 SQLite 数据库,其表如下所示:
CREATE TABLE items (item TEXT, position INTEGER)

该表包含数百万个条目。该列position已编入索引。

我的前端偶尔会构建一堆更新以应用于该position列。要更新的行不是由它们的 id 标识的,因为更新可以在广泛的项目上,并且列出所有相关的 id 可能非常昂贵。例如,可以更新操作,例如“将 +10 添加到位置 >= 500 & 位置 <= 10000” - “添加 -3 到位置 >= 100000 & 位置 <= 100003”等。

问题: 一束更新的条件位置,都是基于一束更新前的位置值。如果我按顺序执行捆绑更新的每个更新操作,那么在一次更新之后可能(实际上存在)一个“重叠”问题。

举例:

item | position
it1  | 1
it2  | 2
it3  | 3
it4  | 4
it5  | 5
it6  | 6
it7  | 7

如果我有这一系列更新:“添加 + 2 到位置 >= 5 和位置 <= 6” - “添加 - 2 到位置 >= 3 和位置 <= 4”,然后我将其转换为这个 SQLite 查询:
UPDATE items SET position=position-2 WHERE position >= 5 AND position <= 6
UPDATE items SET position=position+2 WHERE position >= 3 AND position <= 4

我会得到这个结果:

 it1 | 1
 it2 | 2
 it3 | 5
 it4 | 6
 it5 | 5
 it6 | 6
 it7 | 7

这不是我想要的,而是说:

 it1 | 1          {  it1 | 1  }
 it2 | 2          {  it2 | 2  }
 it3 | 5          {  it5 | 3  }
 it4 | 6    ===>  {  it6 | 4  }
 it5 | 3          {  it3 | 5  }
 it6 | 4          {  it4 | 6  }
 it7 | 7          {  it7 | 7  }

这是因为从一项操作到另一项操作的“重叠”。

我的第一个想法是像这样使用 CASE :

UPDATE items SET position=CASE WHEN position >= 5 AND position <= 6 THEN position-2 WHEN position >= 3 AND position <= 4 THEN position+2 ELSE position END
该解决方案工作正常,但速度非常慢,因为 SQLite 似乎在我的表的数百万个条目上执行此操作,即使主要方不关心更新。

所以我这样修改:
UPDATE items SET position=CASE WHEN position >= 5 AND position <= 6 THEN position-2 WHEN position >= 3 AND position <= 4 THEN position+2 ELSE position END WHERE (position >= 5 AND position <= 6) OR (position >= 3 AND position <= 4)
这个解决方案工作正常而且速度非常快,因为 SQLite 只在相关行上执行更新。

问题: 由于我可以在一组更新中执行 10 甚至 100 次更新操作,因此查询可能会变得非常大,这看起来不太……“漂亮”/“漂亮”。

你认为有一个更漂亮的方法来处理这个吗?SQLite 能否通过第一次选择受影响的行来执行一些“更新”查询,然后在之后有效地更新该行?

想法?想法 ?

非常感谢 !

4

2 回答 2

0

欢迎来到 SO。

我觉得有替代解决方案,但根据您的问题,我会更改您的架构:

CREATE TABLE items (item TEXT, position INTEGER, position_tmp INTEGER)

像这样运行您的更新:

UPDATE items SET position_tmp = position + 1 WHERE 100 <= position AND position < 200
UPDATE items SET position_tmp = position - 3 WHERE 500 <= position AND position < 1000
...

最后,

UPDATE items SET position = position_tmp

永久拥有那个额外的列(确实如此)似乎令人不快position_tmp,但 SQLite 不支持重命名或删除列,即使支持,保留它也更高效。

编辑:

我相信我现在明白你为什么要这样做了。您有数以百万计的项目要强加订单。如果从列表中间删除一个,则必须调整其余部分以反映新顺序。

根据您的需要,可以使用浮点数对它们进行排序。例如,0.1、2.5、5.0。如果你想插入一些东西作为第二个元素,插入位置 1.3。如果要删除最后一个元素,只需删除它即可。

否则,您基本上是在处理一个古老的问题,即拥有一个快速插入和删除的列表。

于 2013-10-27T05:19:26.037 回答
0

You must see your problem algorithmically:

Adding A to items from position P1 to P2:

positions in [P1, P2] will become [P1+A, P2+A]
positions in [P2+1, P2+A] will become [P1, P1+A-1] (shift: -(P2-P1+1)=P1-P2-1)

SQL:

UPDATE items SET position=CASE position <= P2 THEN position+A ELSE position-P1-P2-1 WHERE position BETWEEN P1 AND P2+A;

For subtracting S to items from position Q1 to Q2:

positions in [Q1, Q2] will become [Q1-S, Q2-S]
positions in [Q1-S, Q1-1] will become [Q2-S+1, P2] (shift: P2-P1+1)

SQL:

UPDATE items SET position=CASE position >= Q1 THEN position-S ELSE position+P2-P1+1 WHERE position BETWEEN Q1-S AND Q2;

So, using one of these two queries, you will be able to update all relevant positions in a single statement. Just use appropriate (P1, P2, A) or (Q1, Q2, S).

于 2013-10-28T10:26:00.493 回答