Использование SQLite FTS3 со столбцами INTEGER - PullRequest
11 голосов
/ 14 июля 2011

Я бы хотел использовать SQLite FTS3 (фактически FTS4) для индексации таблицы с целочисленными столбцами, концептуально что-то вроде этого:

CREATE VIRTUAL TABLE whole (document INTEGER, page INTEGER, content TEXT, 
    UNIQUE(document, page)) USING fts4();

Я знаю, что FTS3 обрабатывает все столбцы, кроме rowid, как TEXT, поэтому мне придется использовать две таблицы:

CREATE VIRTUAL TABLE data USING fts4();
CREATE TABLE metadata(document INTEGER, page INTEGER, UNIQUE(document, page));

Я хочу иметь возможность запрашивать документы или страницы в данном документе:

SELECT DISTINCT document FROM metadata NATURAL JOIN data WHERE content MATCH 'foo';
SELECT page FROM metadata NATURAL JOIN data 
    WHERE document = 123 AND content MATCH 'foo';

Я думаю, что ЕСТЕСТВЕННОЕ СОЕДИНЕНИЕ требует от меня обеспечения синхронизации строк, но каков наилучший способ сделать это? Должен ли я использовать FOREIGN KEY или другое ограничение? Будет ли дополнительный выбор лучше, чем объединение?

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

Я также хочу УДАЛИТЬ ИЗ обеих таблиц для данного документа - есть ли способ сделать это в одном выражении?

Все советы с благодарностью получены, но, поскольку я новичок в SQL, примеры кода особенно ценятся!

Обновление: Мне совсем не ясно, как я могу создать здесь ограничение внешнего ключа. Если я выберу metadata в качестве родительской таблицы (что было бы моим предпочтением при отсутствии двунаправленного ограничения):

PRAGMA foreign_keys = ON;
CREATE TABLE metadata (document INTEGER, page INTEGER);
CREATE VIRTUAL TABLE data USING fts4(content TEXT, docid REFERENCES metadata);

Я получаю Error: vtable constructor failed: data (неудивительно, потому что docid является псевдонимом для rowid, но, конечно, я не могу использовать другой столбец, потому что все столбцы, кроме rowid, должны быть TEXT).

Принимая во внимание, если я попробую наоборот:

PRAGMA foreign_keys = ON;
CREATE VIRTUAL TABLE data USING fts4();
CREATE TABLE metadata (document INTEGER, page INTEGER, docid REFERENCES data);

Построение таблицы успешно, но если я попытаюсь:

INSERT INTO data (docid, content) VALUES (123, 'testing');
INSERT INTO metadata (docid, document, page) VALUES (123, 12, 23);

Я получаю Error: foreign key mismatch.

Ответы [ 2 ]

7 голосов
/ 18 июля 2011

Короче говоря, довольно сложно обеспечить согласованность, когда задействован FTS3.

Виртуальные таблицы SQLite не допускают триггеров, а таблицы FTS3 игнорируют ограничения и сходства.

Лучшее, что я смог сделать на данный момент, это:

CREATE TABLE metadata (document INTEGER, page INTEGER, UNIQUE(document, page));
CREATE VIRTUAL TABLE data USING fts4();

CREATE VIEW whole AS SELECT metadata.rowid AS rowid, document, page, content 
    FROM metadata JOIN data ON metadata.rowid = data.rowid;

CREATE TRIGGER whole_insert INSTEAD OF INSERT ON whole
BEGIN
  INSERT INTO metadata (document, page) VALUES (NEW.document, NEW.page);
  INSERT INTO data (rowid, content) VALUES (last_insert_rowid(), NEW.content);
END;

CREATE TRIGGER whole_delete INSTEAD OF DELETE ON whole
BEGIN
  DELETE FROM metadata WHERE rowid = OLD.rowid;
  DELETE FROM data WHERE rowid = OLD.rowid;
END;

Для обеспечения согласованности я мог бы (с помощью PRAGMA recursive_triggers = NO) создавать триггеры, чтобы вызывать исключения для прямых операций с таблицами metadata и data, но это, вероятно, излишне для моих целей (аналогично, мне не нужно UPDATE триггер для таблицы whole).

0 голосов
/ 16 июля 2011

Я думаю, что большинство администраторов баз данных согласятся с тем, что если вы используете SQL, вы должны использовать все возможности, которые он предлагает.

Иностранные ключи - это общий маршрут, который я бы рекомендовал, и они задокументированы здесь .

В общем случае с базами данных SQL вам никогда не следует вручную обеспечивать согласованность. Особенно в случаях, которые подходят для внешнего ключа, такого как этот.

В случае DELETE FROM SQLite не поддерживает ключевое слово "cascade", например, скажем, MS SQL, но имеет триггеры, которые в любом случае позволяют вам иметь такое поведение. Документацию по триггерам SQLite можно найти здесь .

Наконец, я бы пропустил естественное соединение.

...