将SQLite FTS3与INTEGER列一起使用

时间:2011-07-13 22:31:27

标签: sql join sqlite fts3

我想使用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';

我认为NATURAL JOIN要求我确保rowid保持同步,但最好的方法是什么?我应该使用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(不出所料,因为docidrowid的别名,但我当然不能使用其他列,因为除了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 个答案:

答案 0 :(得分:7)

简而言之,在涉及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)创建触发器以在metadatadata表上引发直接操作的异常,但这对我的目的来说可能有点过分(同样,我不需要UPDATE表的whole触发器。

答案 1 :(得分:-1)

我认为大多数DBA会同意,如果你使用SQL,你应该利用它提供的所有功能。

外键是我推荐的一般路线,记录在案here

一般而言,对于SQL数据库,您永远不应该手动强制执行一致性。特别是在适合这种外键的情况下。

对于DELETE FROM案例,SQLite不支持像“MS SQL”这样的“cascade”关键字,但它具有允许您无论如何都具有此行为的触发器。可以找到SQLite触发器的文档here

最后,我会跳过自然联接。