繁体   English   中英

在SQLite数据库中存储订单的好方法是什么

[英]What is a good way to store order in an SQLite database

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

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

该系统的问题是并发性和可维护性:如果同时更新某行的位置(并且随后各行的位置增加/减少),则同步任务时可能会发生混乱,因为同步也更改了行位置。

我可以添加Mutex锁,但是我不想这样做,我希望用户能够在同步的同时更新数据,如果发生冲突,则将丢弃新位置之一,而不会接下来的行被弄乱了。

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

我不知道“最佳方式”。

但是,就像在另一个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;

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

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

另一种处理方式可能是将订单作为一个值存储在不同的表中,例如作为项目ID的列表存储。 这样做的好处是,可以更精确地控制排序,而不会因交错更新而出现乱码状态,但是更为复杂(处理丢失或未知的项目,如何同时处理两个更新)。 除非您真的需要,否则我不会尝试。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM