2013-11-14 3 views
2

Я разрабатываю и поддерживаю приложение Google Tasks на Android. В настоящее время у меня есть класс, который содержит списки и задачи, в порядке, в ArrayList. Я собираюсь перейти на SQLite, чтобы лучше структурировать приложение. Я не уверен, что лучший и простой способ сохранить порядок задач в базе данных будет.Что такое хороший способ сохранить заказ в базе данных SQLite

Прямо сейчас, я могу просто remove и add элементов с разными индексами, и, очевидно, другие индексы строк списка находятся в правильном порядке. С базой данных SQLite я могу сохранить позиционный номер в каждой строке, и каждый раз, когда я перемещаю задачу, обновляю следующие строки соответственно.

Проблема с этой системой заключается в параллелизме и ремонтопригодности: если позиция строки обновляется (и позиции последующих строк увеличиваются/уменьшаются), в то время как синхронизация задач происходит, она может стать беспорядочной, поскольку синхронизация изменение позиций строк.

Я мог бы добавить блокировку Mutex, но я не хочу этого делать, я хочу, чтобы пользователи могли обновлять данные одновременно с синхронизацией, и если возник конфликт, то одна из новых позиций будет отброшен, без перепутанных строк.

Мой вопрос: Каков наилучший способ хранения и обновления порядка в базе данных SQLite?

ответ

0

Проблемы с добавлением значения sort_position к каждой строке в том, что она не может быть обновлена ​​атомарна, так как поменять два элемента в списке вам нужно изменить обе свои позиции. База данных намного лучше гарантирует атомарность, когда изменения относятся к одной и той же строке.

Лучшим способом было бы обработать столбец упорядочения в качестве приоритета - значения могут быть разреженными, так что строки могут быть размещены между двумя соседними, а два элемента могут иметь одинаковое значение, не будучи проблемой согласованности. Если значения являются разреженными, вы можете поменять местами два элемента, просто изменив одно значение, поэтому одновременные обновления обрабатываются более грациозно (хотя вы все равно можете получить неожиданные ситуации, если два агента пытаются переупорядочить одну и ту же часть списка).

Альтернативный способ обращения с ним может заключаться в том, чтобы заказ хранился как одно значение в другой таблице - например, в виде списка идентификаторов элементов. Это имеет то преимущество, что позволяет более точно контролировать порядок, даже когда он не получает искаженное состояние из чередующихся обновлений, но более сложный (обработка отсутствующих или неизвестных элементов, как обрабатывать оба обновления в одно и то же время). Я бы не пробовал этого, если вам это действительно не нужно.

+0

Я не уверен, как относиться к ним как к приоритету, так что вам нужно будет только изменить одно значение. Например, если A является приоритетом 1, B является приоритетом 2, и я хочу поставить C между A и B, мне нужно сдвинуть B на 3 и C на 2. – FaultException

+0

Вот что я имел в виду, имея возможность иметь приоритет значения будут разрежены - пусть они не будут последовательными.Вы не можете сделать это, если все значения последовательны, но если они разделены, скажем, 100, то вы можете легко установить его как значение между приоритетом элементов, которые должны появляться после и после. – babbageclunk

+0

Вот как работает API Tasks Google, но у вас не хватит места, не так ли? Например, если ваш список выглядел так: 0, 1, 3, 6, 12, 25, 50, 100 (последняя задача.) Вы не могли добавить задачу в начале, так как позиция была бы 0/2, что также должно быть 0. – FaultException

0

«Лучший способ» Я не знаю.

Но, как и в другой СУБД, добавление столбца для такого решения является хорошим решением.

Обработка этого столбца подразумевает некоторый код, но в 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; 
+0

Как атомный метод? – FaultException

+0

IFAIK, это атомный, имеет любую операцию, обрабатываемую триггером или успешно завершающуюся или полностью откатывается назад. –

Смежные вопросы