![]() |
Datenbank: Firebird • Version: 2.5 • Zugriff über: IBX
SQL Script optimieren damit es schneller geht
Hallo Leute,
ich habe hier ein Problem mit einem Select und bekomme es einfach nicht hin bzw. es dauert zu lange. Ich habe zwei Tabellen Tabelle1 mit 477.379 Datensätzen Tabelle2 mit 7.087.230 Datensätzen In beiden Tabelle sind zwei Felder dessen Inhalt in beiden gleich ist. Ich möchte einfach alle Datensätze aus der Tabelle1 bei den in der Tabelle2 kein Datensatz vorhanden ist bei dem die beiden Feler übereinstimmen. Es geht zwar jedoch dauert es ca. 17 - 20 Minuten bis das Ergebnis vorliegt. Auf beiden Felden liegt natürlich ein Index. Ich habe schon alles möglich versucht. Sogar schon mitteles Left Outer Join aus reiner Verzweifelung. Das ganze bringt nichts es dauert immer gleich lange. Ich gebe zu es sind einige Datensätze in der Datenbank. Das Ergebnis ist jedoch < 1000 beistens sogar < 300 Hier mal einer meiner vielen Select (Der mit Left Outer Join was nicht unbedingt Sinn macht)
Delphi-Quellcode:
Select Tabelle1.MasterNummer
from Tabelle1 Left Outer Join Tabelle2 on Tabelle1.Staus = Tabelle2.Status and Tabelle1.CoNummer = Tabelle2.CoNummer where Tabelle2.Status is null and Tabelle1.Status <> 'N' and Tabelle1.Storno is Null Altuelle Daten : 160 Datensätze wurde gefunden und es hat etwas über 18 Minuten gedauert. Kann mir jemadn hier helfen bzw gibt es überhaupt eine Lösung für mein Problem Tanja |
AW: SQL Script optimieren damit es schneller geht
Hallo,
Tabelle1.Status <> 'N' kann das ersetzt werden durch ein Tabelle1.Status = 'J' Tabelle2 on Tabelle1.Status = Tabelle2.Status and Tabelle1.CoNummer = Tabelle2.CoNummer Tabelle1.Storno is Null Damit hätten wir also 5 Felder und 5 Indizes, ja? Mal die Indizes neu erzeugt (Alter Index I_X Inactive/Active) Wie sieht der Query-Plan aus (z.B. mit IBExpert-Personal ermitteln? |
AW: SQL Script optimieren damit es schneller geht
Zitat:
Hallo es werden leider nicht alle 5 Indizes benutzt trotz das welche vorhanden sind. Ich habe Sie auch schon mal neu erzeugt mittels Recompute Ein Status = J kann ich nicht setzte da es hier verschieden Inhalte gibt. Ich mache hier auch nun ein N um die Anzahl einzugrenzen da bei dem Status N kein Datensatz in der zweiten Tebelle sein kann. Hier der Query-Plan Plan PLAN JOIN (Tabelle1 INDEX (IDX_Tabelle1_STORNO), Tabelle2 INDEX (IDX_STATUS)) ------ Performance info ------ Prepare time = 592ms Execute time = 18m 38s 933ms Avg fetch time = 186.488,83 ms Current memory = 15.854.640 Max memory = 15.867.656 Memory buffers = 800 Reads from disk to cache = 353.755 Writes from cache to disk = 0 |
AW: SQL Script optimieren damit es schneller geht
ich hab einmal etwas umformatiert:
SQL-Code:
bis zum Join ist ja noch alles in Ordnung aber dann
Select Tabelle1.MasterNummer
from Tabelle1 Left Outer Join Tabelle2 on Tabelle1.Staus = Tabelle2.Status and Tabelle1.CoNummer = Tabelle2.CoNummer where 1=1 and Tabelle2.Status is null and Tabelle1.Status <> 'N' and Tabelle1.Storno is Null Wenn
SQL-Code:
wahr ist, dann viel Spaß beim Joinen!
Tabelle2.Status is null
Wenn
SQL-Code:
wahr ist, dann kannst Du die vorherige Bedingung vergessen.
Tabelle1.Status <> 'N'
Das Da überhaupt etwas heraus kommt wundert mich. Gruß K-H |
AW: SQL Script optimieren damit es schneller geht
Zitat:
Ja es funktioniert genau so wie es soll. Es dauert halt recht lange. Hast du eventuell eine bessere Lösung für mein Problem. Wie gesagt ich suche alle Datensätze in der Tabelle1 wo kein Datensetz in der Tebelle2 vorhanden ist. Der Vergleich muss über zwei Felder erfolgen. Danke Tanja |
AW: SQL Script optimieren damit es schneller geht
Hätte da eine etwas schwerer lesbare Alternative:
SQL-Code:
Unter Oracle haben solche Konstrukte schonmal die eine oder andere Stunde eingespart.
select a.MasterNummer from
( select MasterNummer, Status, CoNummer from Tabelle1 where Tabelle1.Status <> 'N' and Tabelle1.Storno is Null ) a, ( select Status, CoNummer from Tabelle2 where Status is null ) b where a.Status = b.Status and a.CoNummer = b.CoNummer Ist das eventuell bei FireBird ähnlich? Warum dieser Weg? Zuerst soll die Datenbank nur die Sätze aus Tabelle 1 holen, die wir benötigen. Dann dito. für Tabelle 2. Damit sind diese beiden Ergebnismengen erstmal auf das benötigte Mindestmaß "eingestampft". Mit dem "außendrum" liegenden Statement müssen jetzt "nur noch" diese beiden Restmengen nebeneinander gelegt werden. D. H.: Die Menge der miteinander zu vergleichenden Daten wird deutlich verkleinert gegenüber einem Left Outer Join über die Gesamtmenge beider Tabellen. Es kann sein, dass das schneller wird, muss aber nicht so sein. 'nen Versuch sollte es aber wert sein. Alternative:
SQL-Code:
Gleicher Grundgedanke:
select a.MasterNummer
from ( select MasterNummer, Status, Conummer from Tabelle1 where Status <> 'N' and Storno is Null ) a Left Outer Join ( select Status, CoNummer from Tabelle2 where Status is null ) b on a.Status = b.Status and a.CoNummer = b.CoNummer Zuerst die Tabellen auf die jeweils höchstens benötigte Teilmenge reduzieren und dann nur noch diese beiden Teilmengen miteinander verknüpfen. Da die erwartete Ergebnismenge sehr klein ist (im Vergleich zur Ausgangsmenge) dürfte es beim Zusammenfassen der Teilmengen nicht mehr relevant sein, dass dort kein Index vorhanden ist und damit "indexlos" verglichen werden muss. Du schreibst Zitat:
SQL-Code:
bzw.
select count(*) from Tabelle1
where Status <> 'N' and Storno is Null
SQL-Code:
Eventuell kann man aus den Mengenangaben noch weitere Rückschlüsse für eine alternative Abfragemöglichkeit ziehen.
select Count(*) from Tabelle2
where Status is null |
AW: SQL Script optimieren damit es schneller geht
Moin,
ich würde es ähnlich machen. Erst die Datenmenge reduzieren und aus beiden tabellen die in Frage kommenden Daten in temporäre Tabllen kopieren. Anschließend dort en paar Indexe neu bauen und dann die Tabellen vergleichen. Bei diesem Szenario könnte man in beiden temp.Tabellen auch enie zusätzliche Spalte anlegen, die die Werte aus Status und CoNummer "concated". Diese Spalten dann mit einem Index versehen dann einfach joinen bz. left join und auf NULL in Tabelle 2 vergeichen. Sollte dann deutlich schneller gehen... |
AW: SQL Script optimieren damit es schneller geht
Hallo,
Zitat:
Aber mal zum Thema zurück: Select Tabelle1.MasterNummer from Tabelle1 Left Outer Join Tabelle2 on Tabelle1.Staus = Tabelle2.Status and Tabelle1.CoNummer = Tabelle2.CoNummer where Tabelle2.Status is null and Tabelle1.Status <> 'N' and Tabelle1.Storno is Null Nur der Form halber Tabelle1.Staus = Tabelle2.Status Das soll Tabelle1.Status heissen, oder ? on Tabelle1.Staus = Tabelle2.Status where Tabelle2.Status is null Tabelle1.Status <> 'N' Das Tabelle2.Status is null macht doch keinen Sinn. Vorher einen Wert vergleichen Tabelle1.Staus = Tabelle2.Status und dann is null ??? Vielleicht kommt der Optimizer dadurch durcheinander? Ich hätte mal gern das komplette DDL der beiden Tabellen (incl. der Indizes) gesehen. Tabelle1.Status <> 'N' Falls die anderen Werte bekannt sind, würde auch ein in Tabelle1.Status in ('J','1') den Index benutzen. |
AW: SQL Script optimieren damit es schneller geht
Ich sehe da einen Widerspruch zwischen dem Statement aus #1 und der Anforderung " Ich möchte einfach alle Datensätze aus der Tabelle1 bei den in der Tabelle2 kein Datensatz vorhanden ist bei dem die beiden Feler übereinstimmen."
Der Ansatz mit left outer join ist dabei richtig, aber die Prüfung nur auf "Tabelle2.Status is null " ist unsicher, weil sie -ohne Kenntnis des Datenmodells- auch von sich aus null sein kann. (Außer das ist per constraint verboten) Sicher wäre die Prüfung auf dem/den Join Feldern selbst, also
Code:
Wenn ich mit meinen Vermutung richtig liege, sind auch die beiden Vorschläge aus #6 diesbezüglich nicht ausreichend. Die Null Prüfung auf T2.Status sollte eigentlich auch auf CoNummer laufen und ergibt ihren Sinn erst nach dem Join
Left Outer Join Tabelle2 on
Tabelle1.Staus = Tabelle2.Status and Tabelle1.CoNummer = Tabelle2.CoNummer where Tabelle2.CoNummer is null and Tabelle2.Status is null Die Fragen nach den Einzelmengen inkl. der einschränkenden Kriterien halte ich auch für sehr wichtig. Sie bieten wichtige Anhaltspunkte, dem Optimizer einen kleinen Tritt zu geben. (Die verwendeten Indizes sehen jedenfalls nicht sehr geschickt gewählt aus) In der Hoffnung, dass die Menge allein durch den Join stark reduziert wird, würde ich das auch zuerst versuchen. (Nebenbei, keines der anderen gefilterten Felder klingt so, als ob selbst ein vorhandener und verwendeter Index viel nützt. 500T Datensätze mit einer handvoll Statuswerte, da lohnt kein Index) Also Step 1 sowas:
Code:
eigentlich nur, um zu wissen wieviel das wird, also schneller
SELECT Tabelle1.MasterNummer FROM Tabelle1
LEFT OUTER JOIN Tabelle2 ON Tabelle1.CoNummer = Tabelle2.CoNummer
Code:
Step 2 (nun wenigstens mit vollständigem Join Kriterium)
SELECT count(*) FROM Tabelle1
LEFT OUTER JOIN Tabelle2 ON Tabelle1.CoNummer = Tabelle2.CoNummer
Code:
Status ins where, um den Optimizer zu lotsen. (Macht besonders Sinn, wenn CoNummer ein (Primär-)Schlüsselfeld ist oder wenigstens Fremdschlüssel und beidseitig indiziert.
SELECT Tabelle1.MasterNummer FROM Tabelle1
LEFT OUTER JOIN Tabelle2 ON Tabelle1.CoNummer = Tabelle2.CoNummer WHERE Tabelle1.Staus = Tabelle2.Status and Tabelle2.CoNummer is null and Tabelle2.Status is null Dieses Statement enthält logisch die korrekte Prüfung der Anforderung "alle Datensätze aus der Tabelle1 bei den in der Tabelle2 kein Datensatz vorhanden ist". Dabei beziehe ich mich auf das in #1 verwendete Joinkriterium über 2 Felder. Wenn das jetzt schneller sein sollte, wird das endgültige Statement hoffentlich nicht langsamer:
Code:
Die Unterteilung ist des Statements ist geraten hat nur einen Sinn, Optimizer Hilfestellung.
select * from (
SELECT Tabelle1.MasterNummer, Tabelle1.Status, Tabelle1.Storno FROM Tabelle1 LEFT OUTER JOIN Tabelle2 ON Tabelle1.CoNummer = Tabelle2.CoNummer WHERE Tabelle1.Staus = Tabelle2.Status and Tabelle2.CoNummer is null and Tabelle2.Status is null) x where x Status <> 'N' and x.Storno is Null Optimizer Hilfestellung kann man aber auch ganz anders machen. a) Selber einen Plan angeben b) Unnütze Indizes löschen, idealerweise bleiben nur die übrig, die verwendet werden sollen Beides ist ohne Angaben zu Werteverteilung in den Filterfeldern, Datenmodell und zur Verwendung der Felder in anderen Abfragen nicht sinnvoll. |
AW: SQL Script optimieren damit es schneller geht
Hallo,
was passiert, wenn Du die letzten beiden where's weglässt? <>"N" hattest du ja selber schon ausgeschlossen. Das Storno kannst Du selber im Code prüfen. |
AW: SQL Script optimieren damit es schneller geht
Zitat:
|
AW: SQL Script optimieren damit es schneller geht
Ich zeige Euch mal wie die Daten in den Tabellen aussehen können. Eventuell wird es dann deutlicher wo mein Fehler leigt.
Also als erstes einmal bei dem Status N kann es keine Zeile in Tabelle2 geben. Es kann auch keine Zeile in Tabelle 2 geben bei dem der Wert des Feldes Status NULL ist. Es liegt ein Index auf den Felder CoNummer und Status der Teabelle1 sowie CoNummer und Status der Tabelle2
Delphi-Quellcode:
Als Ergebnis möchte ich nun haben 1,3,4 und 6. Also alle Datensätze aus Tabelle1 wo kein Datensatz in Tabelle 2 vorhanden ist.
Tabelle1
Masternummer CoNummer Status 1 2514 N 2 2515 Start 3 2516 Warten 4 2517 Produktion 5 2518 Ausgabe 6 2519 Komplett Tabelle2 CoNummer Status 2515 Start 2516 Start 2517 Start 2517 Warten 2518 Start 2518 Warten 2518 Produktion 2518 Ausgabe |
AW: SQL Script optimieren damit es schneller geht
IMHO ist hier nicht viel zu machen.
Für die Verknüpfung mit Tabelle 2 hilft dir kein Index. Fakt ist: Die Indexe greifen nur auf Status und Storno, alle anderen Datensätze von Tabelle 1 werden beim Select "angefasst". Du kannst nur noch sicherstellen, das zur Prüfung auch nur ein Datensatz in Tabelle 2 "angefasst" wird, indem du statt einem Join exists verwendest. Wenn es schnell sein soll, benötigst du IMHO ein zusätzliches indexiertes Feld in Tabelle 1, welches über "after insert update delete" Trigger der Tabelle 2 aktuell gehalten wird. Frank |
AW: SQL Script optimieren damit es schneller geht
Ich bin neugierig, aber leider erfahrungslos mit Firebird. Im Prinzip würde das hier das Ergebnis liefern, oder?
Code:
SELECT * FROM Tabelle1
WHERE NOT EXISTS ( SELECT CoNummer FROM Tabelle2 WHERE Tabelle2.CoNummer = Tabelle1.CoNummer ) |
AW: SQL Script optimieren damit es schneller geht
Zitat:
Will ich nur wissen, ob es etwas gibt oder nicht, dann frage ich mit Exists ab: Beispiele:
SQL-Code:
Das war nach meiner bisherigen Erfahrung immer die schnellste Variante für eine Existenz- bzw. Nichtexistenzprüfung.
/* gibt es da was in Tabelle2 zu Tabelle1? */
select benötigtespalte(n) from tabelle1 where exists (select 1 from tabelle2 where tabelle1.schlüssel = tabelle2.schlüssel) /* gibt es nichts in Tabelle 2 zu Tabelle 1? */ select benötigtespalte(n) from tabelle1 where not exists (select 1 from tabelle2 where tabelle1.schlüssel = tabelle2.schlüssel) Brauche ich sowas nur für kleine Teilmengen der betroffenen Tabellen dann wird daraus sinngemäß:
SQL-Code:
Ja, ich weiß, das wird zuweilen sehr schwer lesbar, aber wenn's hilft, dann ist dem so. Im Zweifelsfalle erstellt man sich für die "innern Selects" halt Views, dann ist der Zugriff wieder wie im ersten Beispiel leicht lesbar, ohne das Laufzeitveränderungen auftreten.
select * from (
select benötigtespalte(n) from tabelle1 where EinschränkendeBedingungen zu Tabelle1 ) a where exists /* Wenns was geben soll */ ( select Schlüsselspalten from ( select Schlüsselspalten from tabelle2 where EinschränkendeBedingungen zu Tabelle2 ) b ) where a.schlüssel = b.schlüssel Zitat:
Meine Erfahrung hat mich jedoch gelehrt, dass der Optimizer hier bei starken Einschränkungen der Daten auf Teilmengen, die dann zu verknüpfen sind, häufig "daneben liegt". Er kann bei der Optimierung (vermutlich) die aus den Einschränkungen resultierenden Teilmengen nicht optimal einschätzen, um den laufzeittechnisch und dateninduziert optimalen Plan zu erstellen. Würde dashier (sinngemäß) den Anforderungen entsprechen?
SQL-Code:
Und ist es auch akzeptabel schnell?
select a.Masternummer from
( select Masternummer from tabelle1 where status <> 'N' and status is not null ) a where not exists ( select 1 from tabelle2 b where a.CoNummer = b.CoNummer ) |
AW: SQL Script optimieren damit es schneller geht
Zitat:
SQL-Code:
Ist meist etwas schneller, kommt aber wohl vor allem auf die konkreten Daten (und die DB) an.
select *
from t1 left join t2 on (t1.id=t2id) where t2.id is null Man sollte immer beide Wege probieren. Gruß K-H |
AW: SQL Script optimieren damit es schneller geht
Zitat:
Im Zweifel gilt bei mir immer: Ausprobieren, bis die (hoffentlich) beste Lösung gefunden wurde. "Die beste Lösung" gibt es nicht. |
AW: SQL Script optimieren damit es schneller geht
In Firebird kann man sich doch bestimmt auch den EXPLAIN PLAN/ANALYSE ausgeben lassen und sieht ob und welcher Index verwendet wird.
Mal probiert den JOIN umzudrehen? Im Beispiel hat Tabelle2 mehr Einträge.
Delphi-Quellcode:
> für jeden Eintrag in Tabelle 1 alle vielen Einträge in Tabelle 2 durchsuchen (NULL steht nicht im Index, also FullScan und Index ignoriert)
FROM Tabelle1 JOIN Tabelle2
Delphi-Quellcode:
> für jeden Eintrag in Tabelle 2 die wenigen Einträge in Tabelle 2 durchsuchen
FROM Tabelle2 JOIN Tabelle1
Oder halt die Variante ohne JOIN.
SQL-Code:
Das SubSelect kann nun einen Index gut ausnutzen, wenn der über CoNummer+Status geht.
SELECT *
FROM Tabelle1 WHERE NOT exists( SELECT true FROM Tabelle2 WHERE Tabelle1.CoNummer = Tabelle2.CoNummer AND Tabelle1.Status = Tabelle2.Status LIMIT 1 ) Keine Ahnung, ob EXISTS schon so schlau ist und die Felder (hier nur ein True) ignoriert, weil sie niemals genutzt werden, und bereits ein LIMIT verwendet, da ja schon ab dem ersten Fund das Ergebnis feststeht. |
AW: SQL Script optimieren damit es schneller geht
Zitat:
|
AW: SQL Script optimieren damit es schneller geht
Würde das in etwa so übersetzen:
Statt
SQL-Code:
könnte dashier gehen:
SELECT *
FROM Tabelle1 WHERE NOT exists( SELECT true FROM Tabelle2 WHERE Tabelle1.CoNummer = Tabelle2.CoNummer AND Tabelle1.Status = Tabelle2.Status LIMIT 1 )
SQL-Code:
Syntaktisch geht das so unter FireBird (grade mal ausprobiert), ob's auch sinnvolle Ergebnisse liefert, kann ich nicht sagen, first 1 1 sieht jedenfalls seltsam aus ;-)
SELECT *
FROM Tabelle1 WHERE NOT exists( SELECT first 1 1 FROM Tabelle2 WHERE Tabelle1.CoNummer = Tabelle2.CoNummer AND Tabelle1.Status = Tabelle2.Status ) |
AW: SQL Script optimieren damit es schneller geht
Da exists sowieso nur ein read macht (sofern die Indexe in Tabelle2 auf status und conummer existieren,
ist IMHO first 1 überflüssig.
SQL-Code:
Frank
select t1.masternummer
from tabelle1 t1 where t1.status <> 'N' and t1.storno is null and not exists ( select * from tabelle2 t2 where t2.status = t1.status and t2.conummer = t1.conummer ) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:45 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