![]() |
Datenbank: MS SQL • Version: 2022 • Zugriff über: FireDAC
Komplexe SQL-Abfrage optimieren
Hallo. Wir haben ein Dokumentenarchiv mit einem einfachen Volltextindex. Die SQL-Abfrage sieht beispielsweise wie folgt aus:
Code:
Kommt ein neues Dokument in die Datenbank, muss dieses über die docid der Abfrage hinzugefügt werden:
SELECT * FROM document
WHERE docid IN (SELECT id.docid FROM ftiwid id INNER JOIN ftiword w ON id.wordid = w.wordid WHERE (w.word LIKE {s gesuchteswort%}) )
Code:
Tests mit Datenbank bis ca. 80.000 Datensätzen sind problemlos und schnell. In einem Archiv mit 200.000 Zeilen tritt jetzt aber ein echtes Problem auf:
SELECT * FROM document
WHERE docid=200001 OR docid IN (SELECT id.docid FROM ftiwid id INNER JOIN ftiword w ON id.wordid = w.wordid WHERE (w.word LIKE {s gesuchteswort%}) ) Die erste Abfrage ist ok und in 2 Sekunden fertig. Die zweite Abfrage mit "WHERE docid=200001 OR" benötigt aber über eine Stunde Zeit! Hat jemand eine Idee, was dahintersteckt? Besten Dank, Harald |
AW: Komplexe SQL-Abfrage optimieren
Hast du dir schon den Query-Execution-Plan angeschaut?
![]() |
AW: Komplexe SQL-Abfrage optimieren
Zunächst wird die größere Datenmenge eine Rolle spielen. Aber das ist wohl offensichtlich :wink:
eine Prüfung auf "in" wird von der DB als "or" für alle gefundenen Werte umgewandelt. Bei vielen Elementen ist das keine besonders optimale Lösung. Falls MS-SQL das intern besser löst, solltest du das zusätzliche Dokument in das "in" integrieren. Also nach dem Select als weitere ID hinzufügen. Bei potentiell vielen Treffern würde ich das umbauen und aus der IN-Prüfung eine EXISTS-PRüfung machen. Also etwa so:
Code:
Du kannst das ja mal testen :wink:
SELECT * FROM document as d
where exists (SELECT id.docid FROM ftiwid id INNER JOIN ftiword w ON id.wordid = w.wordid WHERE (w.word LIKE {s gesuchteswort%}) and id.docid = d.docid) union all -- um das or komplett rauszuschmeißen SELECT * FROM document WHERE docid=200001 |
AW: Komplexe SQL-Abfrage optimieren
Was wird hier eigentlich gemacht?
Zuerst brauchen wir ein bestimmtes Wort (bzw. eine Liste der IDs zu Wörtern, die dem Suchbegriff ähneln):
SQL-Code:
select wordid from ftiword where (w.word like {s gesuchteswort%})
Zu diesem Ergebnis möchten wir alle DocID:
SQL-Code:
Zu diesem Ergebnis benötigen wir nun alle Dokumente:
select id.docid from ftiwid id where exists (
select 1 from ftiword w where (w.word like {s gesuchteswort%}) and id.wordid = w.wordid )
SQL-Code:
Keine Ahnung, ob MSSQL mit so'ner Syntax zurecht kommt.
select * from document a where exists
( select 1 from ftiwid id where exists ( select 1 from ftiword w where (w.word like {s gesuchteswort%}) and id.wordid = w.wordid ) and id.docid = a.docid ) Warum wird im zweiten SQL nach einer konkreten ID gesucht und zusätzlich noch in einer Liste von IDs? Hier würd' ich's dann mit 'nem Union versuchen:
SQL-Code:
Wenn das nicht geht:
select * from document where docid = 200001
union select * from document a where exists ( select 1 from ftiwid id where exists ( select 1 from ftiword w where (w.word like {s gesuchteswort%}) and id.wordid = w.wordid ) and id.docid = a.docid )
SQL-Code:
select * from document where docid = 200001
union SELECT * FROM document WHERE docid IN (SELECT id.docid FROM ftiwid id INNER JOIN ftiword w ON id.wordid = w.wordid WHERE (w.word LIKE {s gesuchteswort%}) ) |
AW: Komplexe SQL-Abfrage optimieren
Nicht raten. Messen.
Schau dir den Execution Plan an, dann weißt du, wo die Zeit liegen bleibt. Gerne übersehen: Der Aufwand für das Locking bei Abfragen. Ergänze mal ein WITH (NOLOCK) nach den Tabellennamen (und sei dir der Konsequenzen bewusst). Nutze den Optimzer, der gibt dir zielgerichtet Tipps zu deiner Query. Ein Index (wenn er denn genutzt wird) kann böse defragmentiert sein - REBUILD. Die Statistiken, die steuern, ob ein Index genutzt wird, können überholt sein - aktualisieren. Erst dann kannst du versuchen, die Query umzubauen: CTEs, temporäre Tabellen etc. ChatGPT ist auch ein guter Tipp, nicht alles funktioniert, aber du bekommst neue Ideen. |
AW: Komplexe SQL-Abfrage optimieren
Die äußere Abfrage liefert nur die Dokumente zurück, deren ID bereits ermittelt wurde.
Ob mit "or", "in" oder "exists" dürfte kaum einen großen Unterschied darstellen. Die eigentliche Bremse könnte die Verknüpfung aller Worte mit allen Dokumenten darstellen. Neben dem verwendeten Index wirkt sich auch der Speicherverbrauch der Abfrage schnell auf die Geschwindigkeit aus.
Code:
Ich würde es mit einem left join versuchen:
SELECT id.docid
FROM ftiwid id INNER JOIN ftiword w ON id.wordid = w.wordid WHERE (w.word LIKE {s gesuchteswort%})
Code:
Mehrfache Rückgabe des selben Dokuments ist hier nicht erwünscht (distinct).
SELECT distinct id.docid
FROM ftiword w left join ftiwid id ON id.wordid = w.wordid WHERE (w.word LIKE {s gesuchteswort%}) Natürlich könnte man die Anzahl der Dokumente hier sinnvoll z.B. auf die ersten 200 begrenzen. |
AW: Komplexe SQL-Abfrage optimieren
Ganz vergessen: Mit Version 2022 hast du ja Zugriff darauf:
![]() |
AW: Komplexe SQL-Abfrage optimieren
Und? Fortschritt? Erkenntnisse?
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:01 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz