3

我在 Android 上开发和维护一个 Google Tasks 应用程序。目前,我有一个包含列表和任务的类,按顺序排列在ArrayList. 我正在考虑切换到 SQLite 以更好地构建应用程序。我不确定在数据库中存储任务顺序的最佳和最简单的方法是什么。

现在,我可以简单地removeadd不同索引处的项目,显然其他列表行索引的顺序正确。使用 SQLite 数据库,我可以在每一行中存储一个位置编号,并且每次移动任务时,相应地更新以下行。

该系统的问题是并发性和可维护性:如果更新行的位置(并且后续行的位置增加/减少)同时发生任务同步,它可能会变得混乱,因为同步也在改变行位置.

我可以添加一个互斥锁,但我不想这样做,我希望用户能够在同步的同时更新数据,如果发生冲突,则丢弃其中一个新位置,而无需以下行被搞砸了。

我的问题:在 SQLite 数据库中存储和更新订单的最佳方式是什么?

4

2 回答 2

0

“最佳方式”我不知道。

但是,就像在另一个 RDBMS 中一样,为此添加一列是一个很好的解决方案。

处理该列将隐含一些代码,但在 SQLite 中,您可以使用触发器在数据库中实现此逻辑。

这里有一个示例,使用一个后台表和一个执行魔术的视图:

-- Main table, where data belongs
CREATE TABLE _t(n, o);
-- Table view, which will handle all inserts, updates and deletes
CREATE VIEW t AS SELECT * FROM _t;

-- Triggers:
-- Raise error when inserting invalid index (out of bounds or non integer)
CREATE TRIGGER t_ins_err INSTEAD OF INSERT ON t
WHEN NEW.o<1 OR NEW.o>(SELECT COUNT()+1 FROM _t) OR CAST(NEW.o AS INT) <> NEW.o
BEGIN SELECT RAISE(ABORT, 'Invalid index!'); END;
-- Increments all indexes when new row inserted in middle of table
CREATE TRIGGER t_ins INSTEAD OF INSERT ON t
WHEN NEW.o BETWEEN 1 AND (SELECT COUNT() FROM _t)+1
BEGIN
    UPDATE _t SET o=o+1 WHERE o>=NEW.o;
    INSERT INTO _t VALUES(NEW.n, NEW.o);
END;
-- Insert row in last when supplied index is NULL
CREATE TRIGGER t_ins_last INSTEAD OF INSERT ON t
WHEN NEW.o IS NULL
BEGIN
    INSERT INTO _t VALUES(NEW.n, (SELECT COUNT()+1 FROM _t));
END;
-- Decrements indexes when item is removed
CREATE TRIGGER t_del INSTEAD OF DELETE ON t
BEGIN
    DELETE FROM _t WHERE o=OLD.o;
    UPDATE _t SET o=o-1 WHERE o>OLD.o;
END;
-- Raise error when updating to invalid index
CREATE TRIGGER t_upd_err INSTEAD OF UPDATE OF o ON t
WHEN NEW.o NOT BETWEEN 1 AND (SELECT COUNT() FROM _t) OR CAST(NEW.o AS INT)<>NEW.o OR NEW.o IS NULL;
BEGIN SELECT RAISE(ABORT, 'Invalid index!'); END;
-- Decrements indexes when item is moved up
CREATE TRIGGER t_upd_up INSTEAD OF UPDATE OF o ON t
WHEN NEW.o BETWEEN OLD.o+1 AND (SELECT COUNT() FROM t)
BEGIN
    UPDATE _t SET o=NULL WHERE o=OLD.o;
    UPDATE _t SET o=o-1 WHERE o BETWEEN OLD.o AND NEW.o;
    UPDATE _t SET o=NEW.o WHERE o IS NULL;
END;
-- Increments indexes when item is moved down
CREATE TRIGGER t_upd_down INSTEAD OF UPDATE OF o ON t
WHEN NEW.o BETWEEN 1 AND OLD.o-1
BEGIN
    UPDATE _t SET o=NULL WHERE o=OLD.o;
    UPDATE _t SET o=o+1  WHERE o BETWEEN NEW.o AND OLD.o;
    UPDATE _t SET o=NEW.o WHERE o IS NULL;
END;

-- Tests:
INSERT INTO t(n) VALUES('a1');
INSERT INTO t(n) VALUES('b1');
INSERT INTO t(n) VALUES('c1');
INSERT INTO t(n) VALUES('d1');
INSERT INTO t VALUES('e1', 5);
INSERT INTO t VALUES('z1', 20);
SELECT * FROM t ORDER BY o;
INSERT INTO t VALUES('b2', 2);
SELECT * FROM t ORDER BY o;
DELETE FROM t WHERE n='b1';
SELECT * FROM t ORDER BY o;
UPDATE t SET o=4 WHERE o=2;
SELECT * FROM t ORDER BY o;
UPDATE t SET o=3 WHERE o=5;
SELECT * FROM t ORDER BY o;
于 2013-11-14T11:50:43.603 回答
0

向每一行添加sort_position值的问题在于它不能自动更新,因为要交换列表中的两个项目,您需要更改它们的两个位置。当更改针对同一行时,数据库在保证原子性方面要好得多。

更好的方法是将排序列视为优先级 - 值可以是稀疏的,因此可以将行放置在两个相邻的行之间,并且两个项目可以具有相同的值而不会成为一致性问题。如果值是稀疏的,您可以通过更改一个值来交换两个项目,因此可以更优雅地处理并发更新(尽管如果两个代理尝试重新排列列表的相同部分,您可能仍然会遇到意外情况)。

处理它的另一种方法可能是将排序存储为不同表中的一个值 - 例如作为项目 ID 列表。这具有允许更精确地控制排序的优点,同时不会从交错更新中获得乱码状态,但更复杂(处理丢失或未知的项目,如何同时处理两个更新)。除非你真的需要,否则我不会尝试。

于 2013-11-14T12:36:34.167 回答