2016-10-14 1 views
0

Gegeben Ich habe einen großen Tisch in meiner lokalen SQLite-Datenbank (ca. 5'000'000 Zeilen) mit einem Schema wie diesesSqlite: Wie beschleunigt man diese Sqlite-Abfragen?

id (integer, einzigartig), z.B. 6543

description (Text), z.B. "any meaningful text"

tags (Text), z.B. "(living)(home)(garden)"

Also, wenn ich brauche alle Einträge zählen getaggt ‚zu Hause‘ und ‚Garten‘, das tue ich

SELECT count(id) FROM tbl WHERE tags LIKE ‘%(home)%' AND tags LIKE '%(garden)%'

Offensichtlich ist dies nimmt wie immer. (Ich testete es mit einer 2'000'000 Einträge Tabelle Version und es dauerte 93 Sekunden ...)

Wäre es klug, neue Spalten einzuführen, eine für jedes Tag (fünf sind max)?

Also würde ich Spalten wie

id | description | tag1 | tag2 | tag3 | tag4 | tag5 34 | blahblah | home |garden| null | null | null

Die entsprechende Abfrage aussehen würde

SELECT count(id) FROM tbl WHERE (tag1 = 'home' OR tag2 = 'home' OR tag3 = 'home' OR tag4 = 'home' OR tag5 = 'home') AND (tag1 = 'garden' OR tag2 = 'garden' OR tag3 = 'garden' OR tag4 = 'garden' OR tag5 = 'garden')

Ist das wirklich besser?

Ich habe einfach keine Ahnung, was das Beste ist. Ich bin sicher, dass es eine Möglichkeit gibt, die Abfragezeit zu verringern ... Aber ich weiß es nicht.

Jede Hilfe würde sehr geschätzt werden!

Oder vielleicht haben Sie eine völlig neue Idee, wie Sie die Daten und/oder Abfrage umstrukturieren, um es schneller zu machen.

Vielen Dank im Voraus!

+1

Es scheint mir, Sie sind ein MN Beziehung zwischen Tags und Tabl modellieren. Sie könnten diese Tags in eine Tabelle einfügen: 'CREATE TABLE tbl_tags (tbl_id INTEGER REFERENCES tbl (id), tag TEXT)'. Oder, wenn es einen festen Satz von Tags gibt, fügen Sie diese in eine Tabelle ein und verweisen Sie auf tbl_tags. Ihr zweiter Ansatz, der fünf "Tag" -Felder verwendet, sollte viel schneller sein als Ihr erster Ansatz. Wenn SQLite ein passendes Tag findet, wird es nicht weiter ausgewertet. Sind Sie sicher, dass Ihre Anwendung nicht mehr als 5 Tags enthält? – Jeremy

+3

Wenn Sie eine Tabelle für Ihre Tags erstellen, können Sie diese verbinden: 'SELECT count (id) FROM tbl INNER JOIN tbl_tags ON tags.tbl_id = tbl.id UND (tags.tag = 'home' OR ...)' – Jeremy

Antwort

0

In dem Moment, in dem Sie eine Bedingung ...<field> LIKE '%<sub-str>%' verwenden, lösen Sie einen vollständigen Tabellenscan und damit die lange Zeit aus. Dennoch, für 2M Datensätze mit einer Zeichenfolge (Feld tags) von sagen wir 64 Zeichen, scheint es mir 93 Sekunden es viel zu lange Zeit. Ich vermute, dass das Problem nicht vom vollständigen Scan herrührt.

Ich würde vorschlagen, dass Sie die Zeit, überprüfen sie die folgende Abfrage dauert:

select count(*) 
    from <tablename> 
where tags like '(home)%' ; 

Für die 2M Reihen, dies nicht als ein paar Sekunden dauern sollte. Wenn dies der Fall ist, würde ich woanders nach Leistungsproblemen suchen.

Darüber hinaus können Sie Ihre Tags wie |living|home|garder|... speichern und die Suche nach tags like '%|home|%' [dies wird Ihr Problem nicht lösen, aber Sie sparen etwas Platz, da Sie )( mit | ersetzen].

EDIT:

Ich habe erkannt, dass man zwei Bedingungen verwenden (WHERE ... AND).Versuchen Sie Folgendes:

SELECT count(id) 
    FROM tbl 
WHERE tags like '%(home)%(garden)%' 
    OR tags like '%(garden)%(home)%' ; 
+0

Tatsächlich hat die von Ihnen vorgeschlagene Abfrage '359468ms' genommen. Ja, 6 Minuten. Offensichtlich scheint das Problem etwas anderes zu sein. Ich habe einfach keine Ahnung, was falsch sein könnte. Ich bin nicht aus der Erinnerung, nur überprüft. Es gibt keine andere Tabelle in der Datenbank. Was könnte ich noch überprüfen oder tun? :( –

+0

Welche der beiden Abfragen, die ich vorgeschlagen habe dauerte 6 Minuten? Wenn die erste ('... wo Tags wie '(home)%'"), dann müssen Sie Ihre Datenbank analysieren, wie Ressourcen überwachen, während die Abfrage ausgeführt wird (CPU, Speicher IO, etc.) – FDavidov

+0

Ja, die erste einfache Abfrage Ich habe die CPU (80% Idle) und die Speicherbelegung überprüft (4GB frei, nur etwa 200MB von Sqlite benutzt) Ich habe gerade die PRAGMA Einstellungen gefunden cache_size erhöht schließlich die Speicherauslastung und reduziert dadurch die Aufrufzeit. Der erste Durchlauf dauert noch eine Weile, aber nachfolgende Abfragen laufen jetzt viel schneller (ungefähr 5 mal, ungefähr). Trotzdem möchte ich das Tabellen-Setup verbessern Tags zu den Vorschlägen der Tabelle bald –

0

Wenn Sie mehr als eine Instanz von der gleichen Sache haben, sollten Sie es in mehrere Zeilen setzen. In diesem Fall wäre dies für die Tags in einer separaten Tabelle führen:

CREATE TABLE tags (
    id INTEGER REFERENCES tbl(id), 
    tag TEXT 
); 

CREATE INDEX tags_index ON tags(tag); 

Sie können dann mit einfachen Lookups auf der tag Spalte die Abfrage tun, die wegen des Index effizient sind:

SELECT count(*) 
FROM tbl 
WHERE id IN (SELECT id FROM tags WHERE tag = 'home') 
    AND id IN (SELECT id FROM tags WHERE tag = 'garden'); 

Alternativ können Sie ein compound query:

SELECT count(*) 
FROM (SELECT id FROM tags WHERE tag = 'home' 
     INTERSECT 
     SELECT id FROM tags WHERE tag = 'garden'); 
+0

Ich verstehe, ich brauche eine separate "Tags" -Tabelle mit ID und Tag. Ich verstehe nicht, wie das dann mit der 'tbl'-Tabelle verbunden wird :(Wie fülle ich die Tag-Tabelle mit jedem Tag in'() '? Ist die Tag-ID eine Zufallszahl oder irgendwie mit dem Eintrag in verknüpft? tbl? Ich habe die 'create table' &' create index' Anweisungen aufgerufen, aber offensichtlich funktioniert die Abfrage nicht out-of-the-box, weil tags-table leer ist .. Eigentlich ist alles, was ich über sql weiß, autodidaktisch und rudimentär Ich weiß nicht, viel mehr als einfache Select-Anweisungen. Bitte beraten, wie Sie vorgehen, um die Daten zwischen den beiden Tabellen zu verknüpfen, ich bin ahnungslos. –

+0

Die ID bezieht sich auf die Elterntabelle; es ist * nicht * der Primärschlüssel der ' Schlagwort-Tabelle. –