![]() |
Datenbank: MySQL • Version: 5 • Zugriff über: UniDac
in-Klausel in großen Tabellen
Hallo Zusammen,
ich komme bei einer Abfrage leider nicht weiter und bitte Euch um Hilfe. Ich habe eine sehr große Tabelle mit Auftragsdaten aus mehrere Jahren. Die Tabelle ich so vereinfacht aufgebaut: AuftragsNr-----Arbeitsgang------Fertigdatum 4710815------------10------------20181215 4710815------------20------------20181218 4710815------------30------------20181223 4710815------------40------------20190110 4710815------------50------------20190115 7878787------------10------------20181015 7878787------------20------------20181018 7878787------------30------------20181023 7878787------------40------------20181110 7878787------------50------------20181115 5656565------------10------------20190115 5656565------------20------------20190118 5656565------------30------------20190123 5656565------------40------------20190210 5656565------------50------------20190215 andere Aufträge. Ich brauche jetzt eine Abfrage, die mir alle Aufträge mit allen Arbeitsgänge auswirft, die bei einem Arbeitsgang das Fertigdatum in 2018 haben. Also soll das Ergebnis so aussehen: AuftragsNr-----Arbeitsgang------Fertigdatum 4710815------------10------------20181215 4710815------------20------------20181218 4710815------------30------------20181223 4710815------------40------------20190110 4710815------------50------------20190115 7878787------------10------------20181015 7878787------------20------------20181018 7878787------------30------------20181023 7878787------------40------------20181110 7878787------------50------------20181115 Den Auftrag 5656565 nicht, weil er keinen Arbeitsgang hat, der in 2018 abgeschlossen wurde. Mein Versuch
Delphi-Quellcode:
Aber weil die Tabelle recht groß ist, raucht mir die Abfrage ab. Hat jemand eine schlauere Lösung, die ich ausprobieren könnte?
select * from tabelle
where AuftragNr in ( select AuftragNr from Tabelle where Year(FertigDatum) = 2018 group by AuftragNr) Vielen Dank Patrick |
AW: in-Klausel in großen Tabellen
eventuell sowas?
SQL-Code:
select * from tabelle a where exists (select 1 from tabelle b where Year(b.Fertigdatum) = 2018 and a.AuftragsNr = b.AuftragsNr)
|
AW: in-Klausel in großen Tabellen
Ist es unbedingt notwendig, eine Unterabfrage zu verwenden?
|
AW: in-Klausel in großen Tabellen
ich sehe da nicht, warum Du ein "where in" brauchst, wenn es auf einundderselben Tabelle abgefragt wird.
Außerdem würde ich ein "where in" -das m.E. nur für sowas wie "enums" gedacht ist bei großen Mengen immer als Join umformen. Zuletzt würde ich niemals bei derartigen Abfragen mit Funktionen wie year() arbeiten. Derartiges immer mit einer Bereichsabfrage "<datefield> between <vondate> and <bisdate>" damit sollte es auf jeden Fall flott sein, wenn auf dem Datefield ein Index liegt und der Zugriff darauf nicht durch andere Sachen vermasselt wird. |
AW: in-Klausel in großen Tabellen
Also eher sowas?
SQL-Code:
select * from tabelle a where exists (
select 1 from tabelle b where a.AuftragsNr = b.AuftragsNr and b.Fertigdatum between CAST('2018-01-01' AS DATE) and CAST('2018-12-31' AS DATE) ) |
AW: in-Klausel in großen Tabellen
Code:
select * from tabelle where Fertigdatum > 20180000 and fertigdatum < 20190000
|
AW: in-Klausel in großen Tabellen
Eher so:
SQL-Code:
select * from tabelle where Fertigdatum >= '2018-01-01' and Fertigdatum <= '2018-12-31'
|
AW: in-Klausel in großen Tabellen
Zitat:
|
AW: in-Klausel in großen Tabellen
Ich will auch mal:
Delphi-Quellcode:
SELECT [Feldliste] FROM [Tabelle] WHERE Fertigdatum BETWEEN :anfang AND :ende
|
AW: in-Klausel in großen Tabellen
sag ich doch
;) |
AW: in-Klausel in großen Tabellen
Sry, im Textfluss übersehen.
|
AW: in-Klausel in großen Tabellen
Es sollen aber alle Sätze zu den AuftragsNr angezeigt werden, zu denen "auch" einen Satz aus dem Jahr 2018 existiert und nicht "nur" die Sätze aus dem Jahr 2018. Man kommt also an einem IN oder einem adäquaten Join-Konstrukt nicht vorbei.
SQL-Code:
hat den Nachteil, dass hier (vermutlich) Fertigdatum zu einer Zeichenfolge konvertiert wird. Damit wird dann eine Indexnutzung (wahrscheinlich) hinfällig. Dabei gehe ich mal davon aus, dass es sich bei Fertigdatum um eine Spalte vom Typ DateTime, Date oder sowas handelt.
select * from tabelle where Fertigdatum >= '2018-01-01' and Fertigdatum <= '2018-12-31'
Und klar: Wenn mal das richtige SQL gefunden wurde, dann erfolgt die Wertübergabe per Parameter und nicht per "festverdrahteter" Konstanten im SQL. |
AW: in-Klausel in großen Tabellen
Sollte da ein einfacher INNER JOIN nicht ausreichen?
|
AW: in-Klausel in großen Tabellen
Zitat:
Also
Code:
@DeddyH: Ich beschwer mich nicht, hab es ja tatsächlich nicht mal halbwegs formatiert und ja.
select *
from Auftragsdaten A join Auftragsdaten A2018 on A.AuftragsNr = A2018.AuftragsNr where A2018.Fertigdatum between :anfang and :ende |
AW: in-Klausel in großen Tabellen
Zitat:
ok, dann so, wenn der Vorgang komplett drin sein sollte ohne Parameter usw. nur schematisch...
Code:
select * from tabelle where (Fertigdatum > 20180000 and fertigdatum < 20190000) and (Fertigdatum < 20180000 or fertigdatum > 20190000)
|
AW: in-Klausel in großen Tabellen
@Jobo: Wird der Join nicht zu groß (Kreuzprodukt)?
Sinngemäß ginge vllt. sowas:
SQL-Code:
Select B.* From
(Select Distinct Auftragsnummer From Tabelle Where Datum between 010118 und 311218) A Left Join Tabelle B On B.Auftragsnummer=A.Auftragsnummer |
AW: in-Klausel in großen Tabellen
In allen mir bekannten DBMS ist ein JOIN ohne weitere Angaben automatisch ein INNER JOIN. Das Kreuzprodukt erhält man mit CROSS JOIN (wobei mir dafür bislang noch kein Anwendungsfall begegnet ist).
|
AW: in-Klausel in großen Tabellen
Oder doch eher dashier?
SQL-Code:
select a.*
from Auftragsdaten A join Auftragsdaten A2018 on A.AuftragsNr = A2018.AuftragsNr where A2018.Fertigdatum between :anfang and :ende |
AW: in-Klausel in großen Tabellen
Hallo Zusammen,
vielen Dank für das toll Feedback!!! Ich habe verschiedene Vorschläge geprüft, aber ich glaube, die richtige Lösung war noch nicht dabei. Meine Schwierigkeit liegt darin, dass ich alle Arbeitsgänge von allen Aufträgen aus einem Jahr aufgelistet haben möchte, wo mindestens 1 Arbeitsgang in dem ausgewählten Jahr fertig geworden ist. Die Arbeitsgänge, die durch einen Jahreswechsel im vorherigen oder nachfolgenden Jahr fertig wurden, möchte ich auch haben. Sobald ein Arbeitsgang in dem ausgewählten Jahr fertig wurde möchte ich immer alle Arbeitsgänge, die zu dem Auftrag gehören ausgewiesen bekommen. Es geht also nicht nur um die Arbeitsgänge, die im ausgewählten Jahr fertig wurden, sondern auch um die "Geschwister-Arbeitsgänge" des Aufrags, egal wann die fertig wurden. Deshalb hatte ich es mit der IN-Funktion probiert... Vielen Dank! Patrick |
AW: in-Klausel in großen Tabellen
Ich bedaure bei solchen SQL-Fragen immer wieder, daß offenbar niemand auf die Idee kommt, ein kurzes Script zum Erzeugen einer Test-DB mitzuliefern. Bei Delphi-Fragen hilft ein Code-Beispiel eine eventuelle Antwort vorher zu testen. Bei SQL-Fragen sind die Antworten in der Regel ungetestet und fallen manchmal eher in die Kategorie "geraten". Die tabellarische Auflistung im ersten Post verdeutlicht zwar das Problem, ist aber zum Austesten nicht wirklich ausreichend.
Am liebsten wäre mir ein kompletter Unit-Test (DUnit oder DUnitX), der lediglich einen entsprechenden "Datenbankserver" voraussetzt, und wo man allenfalls bei der Connection und Authentifizierung etwas anpassen muss. Das käme dann ja auch direkt der Testabdeckung des eigentlichen Projekts zugute. |
AW: in-Klausel in großen Tabellen
Was stimmt den jetzt bei den Vorschlägen nicht: Die Geschwindigkeit oder das Ergebnis?
Alle AuftragsNr aus 2018 werden gesucht:
SQL-Code:
und nun aus der Tabelle alle die, deren AuftragsNr im obigen Ergebnis enthalten ist:
select distinct AuftragsNr from tabelle where Fertigdatum between 20180000 and 20181231
SQL-Code:
Soweit so richtig so langsam?
select * from tabelle where AuftragsNr in (
select distinct AuftragsNr from tabelle where Fertigdatum between 20180000 and 20181231 ) Von welchem Datentyp ist eigentlich Fertigdatum? Und auf welchen der für die Abfrage benötigten Spalten gibt es einen Index bzw. keinen Index? Und wie sehen die entsprechenden Indexdefinitionen aus? Um was für eine Datenmenge handelt es sich? Ein paar Dutzend, ein paar hundert, ein paar Tausend, etliche Millionen? Und ja: Uwe Raabe hat recht: Eigentlich sind die Infos vieeeeeeeel zu dürftig, um sinnvolle Hilfestellung geben zu können. Vollständiges Createstatement für die Tabelle und Mengenangaben (incl. erwarteter Ergebnismenge und ggfls. erwarteten Zuwächsen für die "restliche Lebenszeit des Programmes") wären hilfreich. |
AW: in-Klausel in großen Tabellen
@jumpy, DeddyH
Ich denke, jumpy meint die Mehrfachausgabe der AuftrNr. So ist es sicher und vielleicht auch etwas abgehärtet gegen Optimizer "Irrtümer":
Code:
@uwe: Ich geb Dir Recht, ein Unit Test ist aber vielleicht eher was für die harten Fälle?
select A.*
from Auftragsdaten A join (select distinct AuftragsNr from Auftragsdaten where A2018.Fertigdatum between :anfang and :ende) A2018 on A.AuftragsNr = A2018.AuftragsNr |
AW: in-Klausel in großen Tabellen
Zitat:
Zitat:
Das Fertigatum ist als Date definiert und ich habe einen Index auf den Spalten AuftragsNr, ArbeitsgangNr, RückmeldeDatum. Die Tabelle umfasst ca. 1,7 Mio Datensätze und es kommen jährlich ca. 300.0000 dazu. Ich werde am Montag eine Bereiningung der Daten vornehmen, weil ich bei einigen Datensätzen ein falsches Datumsformat entdeckt habe... Ich melde mich am Montag wieder! Vielen Dank!!! Patrick |
AW: in-Klausel in großen Tabellen
Zitat:
Würdest Du ein Datumstyp nehmen, wäre das andere Problem, von dem Du anschließend schreibst, nicht auftreten. Ändert nichts daran, dass mein letzter Vorschlag helfen soll. Vorausgesetzt es gibt einen Index auf dem Feld und keine anderen Überraschungen. |
AW: in-Klausel in großen Tabellen
SQL-Code:
Damit wir das Problem noch etwas besser verstehen können, bitte eine "Übersetzung" der Spaltennamen "liefern", die Kürzel erscheinen mir doch eher sehr (fehl)interpretationsanfällig.
select concat(waaunr,'-', waaupo) as auftrag,
watenr, oaagnr, oamanr, oarmda, oatlkz, oarmmg, sum(oarmmg * wagewi / 1000) as wagewi from as400archiev a where exists ( select 1 from as400archiev b where Year(b.oarmda) = 2015 and a.waaunr = b.waaunr and a.waaupo = b.waaupo -- hier dürfen nur die Sachen abgefragt werden, -- die die AuftragsNr bilden, alles andere führt zu -- ungewünschten Ergebnissen, da es das Abfrageergebnis -- weiter einschränkt, als erforderlich. -- wegen :Bei dieser Variante scheinen die Arbeitsgänge durch Jahresübergänge verloren zu gehen... -- and a.oaagnr = b.oaagnr -- auskommentiert. ) group by a.waaunr, a.waaupo, a.oaagnr Welche Spalteninhalte sind "jahresübergreifend" und welche nicht? In der Wherebedingung dürfen keine Spalten abgefragt werden, für die die Jahreseinschränkung im Endergebnis nicht relevant ist, sondern nur die für die Ermittlung der Datensätze, die zwingend auch im abgefragten Jahr zu finden sein müssen. Der Arbeitsgang scheint mir hier in der Abfrage daher fehl am Platz. Ausgehend von Deinem Beispiel im Eingangspost enthält er nur ein Fertigdatum und kann daher nicht "jahresübergreifend" vorkommen. Diese Einschränkung dürfte die Ursache für den Verlust der Jahresübergänge sein. (Aber da kann ich mich auch durchaus irren.) Und bei der Datenmenge muss man dann wohl doch sehr auf die Feinheiten der DB und deren Umsetzung der Abfragen achten, sonst wird's doch eher laaaaangsam :-( Achso: Weiß nicht wie das bei MySQL ist, aber dashier Year(b.oarmda) kann Dir eventuell die Nutung des Index kaputtmachen, da ja nur eine Teilmenge des Wertes gebraucht wird. Hier könnte also die Abfrage mit
SQL-Code:
schlimmsten- / bestenfalls so das eine oder andere Stündchen Abfragedauer einsparen.
b.oarmda between CAST('2018-01-01' AS DATE) and CAST('2018-12-31' AS DATE)
Wenn möglich Parameter nutzen, keine Funktionen in die Wherebedingung, die eine Spalte in einen anderen Type konvertieren oder nur Teilmengen einer Spalte nutzen. Lieber ein between mit kleinstem und größtem zulässigen Wert nutzen. |
AW: in-Klausel in großen Tabellen
Zitat:
|
AW: in-Klausel in großen Tabellen
Hallo,
und am besten noch einen Link auf diesen Thread in den Quellcode ;) |
AW: in-Klausel in großen Tabellen
Moin,
ich erschlage solche Schwierigkeiten immer, in dem ich den Abfrageweg vorgebe. Bei manchen Abfragen mit select in, bin ich mir ob der Schlauigkeit von z.B. mysql nicht so sicher. Vorraussetzung für en solches Vorgehen ist natürlich, dass man die Rechte hat, (temporäre) Tabellen zu erzeugen
Code:
Gruß
/* Die relevanten Datensätze separieren */
DROP TABLE IF EXISTS tempt; CREATE TEMPORARY TABLe tempt(select distinct AuftragsNr from Auftragstable where Fertigdatum >= '2018-01-01' and Fertigdatum <= '2018-12-31'); ALTER TABLE tempt ADD index(AuftragsNr); /* Ursprungstabelle mit separierter Datenmenge joinen und voila (jedenfalls voila, wenn es einen Index gibt, der für die AuftragsNr zuständig ist*/ select a.* from Auftragstable a, tempt t where a.AuftragsNr=t.AuftragsNr |
AW: in-Klausel in großen Tabellen
Warum eigentlich ein "in"?
SQL-Code:
so ungefähr, die korrekte Syntax und die between-Grenzen müßtest Du noch überprüfen.
select auftragsdaten.irgendwas
from auftragsdaten join (select distinct auftragsnr from auftragsdaten where fertigdate between 20180101 and 20181231) a on (auftragsdaten.auftragsnr=a.auftrgsnr) Gruß K-H P.S. Zumndest Oracle begrenzt die "in-Menge" auf 2000 Einträge. |
AW: in-Klausel in großen Tabellen
Zitat:
SQL-Code:
mit hier aufgeführen 2000 Werten müsste funktionieren, nicht jedoch
select * from tabelle where Spalte in ('1','2', ... ,'2000')
SQL-Code:
mit hier aufgeführten mehr als 2000 Werten.
select * from tabelle where Spalte in ('1','2', ... ,'2001')
SQL-Code:
sollte auch noch bei ein paar millionen Datensätzen funktionieren, wenn auch nicht unbedingt sehr schnell.
select * from tabelle where Spalte in (select anderespalte from anderetabelle)
Von den hier bisher aufgeführen Lösungsvorschlägen müssten eigentlich einige zum richtigen Ergebnis führen. Einziger Unterschied dürfte die Laufzeit der jeweiligen Abfrage sein. Unter Oracle ware bisher eigentlich meist die Variante mit dem
SQL-Code:
am Schnellsten, da hier nur eine Existenzprüfung nötig ist. Ein Distinct muss die gesamte Datenmenge "durchgehen", sortieren und damit dann eine Eindeutigkeit sicherstellen. Das dauert meist länger, als "nur" nachzuschauen, ob es da überhaupt einen passenden Satz gibt.
... where exists (select 1 from ...
|
AW: in-Klausel in großen Tabellen
Aber muss nicht bei der Distinct + Join Variante der Distinct nur 1x gemacht werden, während beid er exists Variante für jeden Datensatz ein Subselect gemacht werden muss?
|
AW: in-Klausel in großen Tabellen
Ich mag auch mal ;-)
Delphi-Quellcode:
select
A.AuftragsNr , A.Arbeitsgang from Auftrag A join ( select * from ( select Sub.AuftragsNr , count(Sub.Arbeitsgang) Anzahl from Auftrag Sub where (Sub.FertigDatum between 'ANFANG' and 'ENDE') group by Sub.AuftragsNr ) AufAnz where (0 < AufAnz.Anzahl) ) OnlyNotZero on (OnlyNotZero.AuftragsNr = A.AuftragsNr) |
AW: in-Klausel in großen Tabellen
Zitat:
Gruß K-H |
AW: in-Klausel in großen Tabellen
Zitat:
Dort ist eine recht gute Erklärung zu finden: ![]() Zitat:
Unter Oracle war es zu Zeiten, als ich mich damit noch beruflich befasste, deutlich schneller, mit Exists zu arbeiten. Spätestens, wenn die Datenbank den temporary tablespace nutzen musste, konnte man da Unterschiede bemerken, weil irgendwann die Auslagerung des Speichers auf die Festplatte(n) sich im Laufzeitverhalten deutlich bemerkbar machte. Hier im konkreten Fall müssen wir nur wissen, ob es zu 'nem Auftrag mindestens einen Satz im gewünschten Jahr gibt, um dann alle Datensätze zu der Auftragsnummer auszugeben. Mit Exists erhalten wir nur ein Ja oder ein Nein, bei 'nem Join erhalten wir alle Datensätze aus dem gewünschten Jahr. Die werden dann (erstmal) mit allen Datensätzen der Auftragsnummer verbunden. Es kann also (auf Auftragsnummerebene) einen CROSS JOIN geben. (DeddyHs diesbezügliche Anmerkung ist daher angebracht.) Bei einem Join muss man also auch noch sicherstellen, dass keine (partiziellen) Dubletten entstehen. Das ist aufwändiger, als eine "simple" Existenzprüfung. Daher ist (meiner Meinung nach) im vorliegenden Fall Exists vorzuziehen. |
AW: in-Klausel in großen Tabellen
Zitat:
Code:
Ich hatte allerdings auch schon Fälle, wo es nicht anders ging als ein Zwischenergebnis in einer temporären Tabelle zu speichern. In deinem Fall würde man da
select distinct <feldliste> from tabelle
where exists ( select AuftragNr from Tabelle where Year(FertigDatum) = 2018) order by auftragnr, arbeitsgang das Resultat von
Code:
ablegen. Diese Query gibt halt deutlich weniger Zeilen zurück als ohne "distinct", ist aber aufwendiger in der Verarbeitung.
select distinct AuftragNr from Tabelle where Year(FertigDatum) = 2018)
|
AW: in-Klausel in großen Tabellen
Also ich würde folgendes tun:
1. Ein Feld "Jahr" einführen und entsprechend füllen 2. Einen Index für das Feld "Jahr" anlegen 3. Anschließend folgende Abfrage ausprobieren:
Code:
4. Falls das nicht performant ist, dann das Ergebnis der inneren Abfrage vorher in einer (temporären) Tabelle zwischenspeichern (in dieser Tabelle den Index auf AuftragNr nicht vergessen!) und dann die obengenannte Abfrage auf die temporäre Tabelle machen. Das würde dann ungefähr so aussehen:
SELECT * FROM tabelle a
INNER JOIN ( SELECT DISTINCT AuftragNr FROM Tabelle where Jahr = 2018) i ON a.AuftragNr = i.AuftragNr ORDER BY auftragnr, arbeitsgang
Code:
Wenn diese Statements zusammen nicht innerhalb eines Fingeschnippsens durch sind, fresse ich einen Besen... :lol:
CREATE TEMPORARY TABLE `temptable` (
`AuftragNr` int(11), PRIMARY KEY (`AuftragNr`) ) ENGINE=InnoDB; INSERT INTO `temptable` SELECT DISTINCT AuftragNr FROM Tabelle where Jahr = 2018; SELECT * FROM `tabelle` a INNER JOIN `temptable` i ON a.AuftragNr = i.AuftragNr ORDER BY AuftragNr, arbeitsgang; DROP TEMPORARY TABLE `temptable`; PS1: Ich habe nicht alle vorherigen Antworten komplett durchgelesen, vielleicht wiederhole ich auch nur bereits Gesagtes. PS2: Ich habe die Statements nicht ausprobiert. |
AW: in-Klausel in großen Tabellen
PS: Ich gehe doch mal davon aus, dass der Themenersteller in seiner Tabelle auf jeden Fall einen Index auf `AuftragsNr` hat...
|
AW: in-Klausel in großen Tabellen
Die Auftragsnummer wird über
SQL-Code:
gebildet. Sie ist ein Konstrukt mehrerer Werte, die zusammen zu einem gewünschten Jahr existieren müssen. Ist diese Existenz gegeben, müssen alle Sätze, die über dieses Konstrukt verfügen, von der Datenbank herausgesucht werden.
concat(waaunr,'-', waaupo)
|
AW: in-Klausel in großen Tabellen
Zitat:
|
AW: in-Klausel in großen Tabellen
Das ist nur dann nicht performant, wenn man für die Abfrage zuerst die beiden Felder zusammenfasst und diese Zusammenfassung für die Wherebedingung nutzt. Da man die Wherebedingung aber über die beiden Felder separat machen kann, sollte das durchaus schnell mit einer Exists-Prüfung im SQL zu realisieren sein. Klar, der Index muss auf diese Abfrage optimiert sein, dass sollte aber mit 'ner modernen Datenbank im Rahmen des Möglichen sein.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:50 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 by Thomas Breitkreuz