2011-07-13 32 views
11

Me gustaría utilizar SQLite FTS3 (FTS4, en realidad) para indexar una tabla con columnas de tipo entero, conceptualmente algo como esto:FTS3 con columnas INTEGER

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

Sé que FTS3 trata a todos los demás columnas que rowid como texto, así que voy a tener que usar dos tablas:

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

Quiero ser capaz de consultar los documentos, o para páginas en un documento dado:

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'; 

Creo que NATURAL JOIN requiere que me asegure de que los rowids se mantengan sincronizados, pero ¿cuál es la mejor manera de hacerlo? ¿Debería usar una FOREIGN KEY u otra restricción? ¿Sería mejor una sub selección que una unión?

Me gustaría una inserción para un documento y página que ya están en la base de datos para sobrescribir el contenido del texto. ¿Es posible hacerlo mediante SQL o tendré que verificar si la fila ya existe en la tabla de información?

También voy a querer ELIMINAR DE ambas tablas para un documento dado - ¿hay alguna manera de hacer esto en una sola declaración?

Todos los consejos recibidos con gratitud, pero como soy un novato en SQL, los ejemplos de código son especialmente apreciados.

Actualización: No tengo muy claro cómo puedo crear una restricción de clave externa aquí. Si decido metadata como la tabla primaria (que sería mi preferencia, en ausencia de una restricción bidireccional):

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

consigo Error: vtable constructor failed: data (como era de esperar, porque docid es un alias para rowid, pero por supuesto que puedo no use otra columna porque todas las columnas excepto rowid deben ser TEXT).

Mientras que si lo intento a la inversa:

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

la construcción de la tabla tiene éxito, pero si trato:

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

consigo Error: foreign key mismatch.

+0

¿Se las arregló para crear tablas virtuales con claves externas? – Maragues

+0

@Maragues No, y tampoco puedes hacer cumplir las mismas restricciones; consulta mi respuesta a continuación. Las tablas virtuales AFAICT son solo programas con interfaces SQL. – hatfinch

Respuesta

6

En resumen, es bastante difícil imponer la coherencia cuando se trata de FTS3.

Las tablas virtuales SQLite no permiten desencadenantes, y las tablas FTS3 ignoran las restricciones y afinidades.

El mejor que he podido hacer hasta ahora es la siguiente:

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; 

Hacer cumplir la consistencia que pude (con PRAGMA recursive_triggers = NO) crear disparadores para elevar excepciones en operaciones directas en las metadata y data tablas, pero esto es probablemente excesivo para mis propósitos (del mismo modo, no necesito el disparador UPDATE para la tabla whole).

-1

Creo que la mayoría de los DBA estarían de acuerdo en que si está utilizando SQL, debería aprovechar todas las facultades que ofrece.

Llaves externas son la ruta general que recomendaría y están documentadas here.

En general, con las bases de datos SQL, nunca debe aplicar consistencia de forma manual. Particularmente en los casos que se ajustan bien con una clave externa como esta.

Para el caso DELETE FROM, SQLite no admite la palabra clave "cascada" como decir MS SQL, pero tiene activadores que le permiten tener este comportamiento de todos modos. La documentación para los activadores de SQLite se puede encontrar en here.

Por último, me saltaría hacer la unión natural.

+1

Aparentemente SQLite ahora es compatible con DELETE en cascada, pero no sirve de mucho porque no puedo hacer que funcione la restricción de clave externa (consulte la actualización anterior). PD Podría confirmar: cuando dice "omitir hacer la unión natural", ¿quiere decir utilizar una sub selección en su lugar? – hatfinch

+2

Las tablas FTS no admiten claves foráneas o índices de ningún tipo que no sean 'docid' /' rowid' – Greyson