Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   in-Klausel in großen Tabellen (https://www.delphipraxis.net/199645-klausel-grossen-tabellen.html)

Ykcim 8. Feb 2019 10:58

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:
select * from tabelle
where AuftragNr in (
select AuftragNr from Tabelle where Year(FertigDatum) = 2018 group by AuftragNr)
Aber weil die Tabelle recht groß ist, raucht mir die Abfrage ab. Hat jemand eine schlauere Lösung, die ich ausprobieren könnte?

Vielen Dank
Patrick

Delphi.Narium 8. Feb 2019 11:10

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)

DasWolf 8. Feb 2019 11:15

AW: in-Klausel in großen Tabellen
 
Ist es unbedingt notwendig, eine Unterabfrage zu verwenden?

jobo 8. Feb 2019 11:16

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.

Delphi.Narium 8. Feb 2019 11:24

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)
)

ibp 8. Feb 2019 11:52

AW: in-Klausel in großen Tabellen
 
Code:
select * from tabelle where Fertigdatum > 20180000 and fertigdatum < 20190000

DasWolf 8. Feb 2019 11:56

AW: in-Klausel in großen Tabellen
 
Eher so:

SQL-Code:
select * from tabelle where Fertigdatum >= '2018-01-01' and Fertigdatum <= '2018-12-31'

haentschman 8. Feb 2019 12:01

AW: in-Klausel in großen Tabellen
 
Zitat:

Eher so:
Nö...nur mit Parametern aktzeptabel. :zwinker:

DeddyH 8. Feb 2019 12:04

AW: in-Klausel in großen Tabellen
 
Ich will auch mal:
Delphi-Quellcode:
SELECT [Feldliste] FROM [Tabelle] WHERE Fertigdatum BETWEEN :anfang AND :ende

jobo 8. Feb 2019 12:05

AW: in-Klausel in großen Tabellen
 
sag ich doch
;)

DeddyH 8. Feb 2019 12:06

AW: in-Klausel in großen Tabellen
 
Sry, im Textfluss übersehen.

Delphi.Narium 8. Feb 2019 12:07

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:
select * from tabelle where Fertigdatum >= '2018-01-01' and Fertigdatum <= '2018-12-31'
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.

Und klar: Wenn mal das richtige SQL gefunden wurde, dann erfolgt die Wertübergabe per Parameter und nicht per "festverdrahteter" Konstanten im SQL.

DeddyH 8. Feb 2019 12:16

AW: in-Klausel in großen Tabellen
 
Sollte da ein einfacher INNER JOIN nicht ausreichen?

jobo 8. Feb 2019 12:19

AW: in-Klausel in großen Tabellen
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1425064)
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.

Ja, Du hast Recht, hab ich übersehen.
Also
Code:
select *
  from Auftragsdaten A
  join Auftragsdaten A2018
    on A.AuftragsNr = A2018.AuftragsNr
 where A2018.Fertigdatum between :anfang and :ende
@DeddyH: Ich beschwer mich nicht, hab es ja tatsächlich nicht mal halbwegs formatiert und ja.

ibp 8. Feb 2019 12:29

AW: in-Klausel in großen Tabellen
 
Zitat:

Zitat von ibp (Beitrag 1425058)
Code:
select * from tabelle where Fertigdatum > 20180000 and fertigdatum < 20190000


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)

Jumpy 8. Feb 2019 12:38

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

DeddyH 8. Feb 2019 12:44

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).

Delphi.Narium 8. Feb 2019 12:47

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

Ykcim 8. Feb 2019 12:53

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

Uwe Raabe 8. Feb 2019 13:01

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.

Delphi.Narium 8. Feb 2019 13:08

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:
select distinct AuftragsNr from tabelle where Fertigdatum between 20180000 and 20181231
und nun aus der Tabelle alle die, deren AuftragsNr im obigen Ergebnis enthalten ist:
SQL-Code:
select * from tabelle where AuftragsNr in (
  select distinct AuftragsNr from tabelle where Fertigdatum between 20180000 and 20181231
)
Soweit so richtig so langsam?

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.

jobo 8. Feb 2019 13:15

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:
select A.*
  from Auftragsdaten A
  join (select distinct AuftragsNr
          from Auftragsdaten
         where A2018.Fertigdatum between :anfang and :ende) A2018
    on A.AuftragsNr = A2018.AuftragsNr
@uwe: Ich geb Dir Recht, ein Unit Test ist aber vielleicht eher was für die harten Fälle?

Ykcim 8. Feb 2019 13:39

AW: in-Klausel in großen Tabellen
 
Zitat:

select * from tabelle where AuftragsNr in (
select distinct AuftragsNr from tabelle where Fertigdatum between 20180000 and 20181231
)
Bei diesem Code raucht mir die Datenbank ab - wird und wird nicht fertig....

Zitat:

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 and a.oaagnr = b.oaagnr)
group by a.waaunr, a.waaupo, a.oaagnr
Bei dieser Variante scheinen die Arbeitsgänge durch Jahresübergänge verloren zu gehen...

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

jobo 8. Feb 2019 13:46

AW: in-Klausel in großen Tabellen
 
Zitat:

Zitat von Ykcim (Beitrag 1425088)
Zitat:

select * from tabelle where AuftragsNr in (
select distinct AuftragsNr from tabelle where Fertigdatum between 20180000 and 20181231
)
Bei diesem Code raucht mir die Datenbank ab - wird und wird nicht fertig....
.....snipp...
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!

Ok, das bedeutet, Dein Datumsfeld ist ein Ganzzahltyp?!

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.

Delphi.Narium 8. Feb 2019 14:21

AW: in-Klausel in großen Tabellen
 
SQL-Code:
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
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.

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:
b.oarmda between CAST('2018-01-01' AS DATE) and CAST('2018-12-31' AS DATE)
schlimmsten- / bestenfalls so das eine oder andere Stündchen Abfragedauer einsparen.

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.

Uwe Raabe 8. Feb 2019 14:27

AW: in-Klausel in großen Tabellen
 
Zitat:

Zitat von jobo (Beitrag 1425082)
@uwe: Ich geb Dir Recht, ein Unit Test ist aber vielleicht eher was für die harten Fälle?

Die Vielzahl der Vorschläge hier, die nicht das gewünschte Ergebnis liefern, rechtfertigt meiner Meinung nach einen Unit Test in jedem Fall. Ich bin mir nicht sicher, ob man als neuer Entwickler oder selbst nach einer gewissen Zeit noch anhand der SQL-Anweisung das beabsichtigte Ergebnis ersehen kann. Sollten dann Anpassungen oder Refactorings (z.B. auf einen anderen SQL-Server-Typ) notwendig sein, wird man für einen solchen Test sicher dankbar sein.

hoika 8. Feb 2019 14:38

AW: in-Klausel in großen Tabellen
 
Hallo,
und am besten noch einen Link auf diesen Thread in den Quellcode ;)

stifflersmom 8. Feb 2019 16:46

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:
/* 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
Gruß

p80286 8. Feb 2019 20:39

AW: in-Klausel in großen Tabellen
 
Warum eigentlich ein "in"?

SQL-Code:
select auftragsdaten.irgendwas
from auftragsdaten join (select distinct auftragsnr from auftragsdaten where fertigdate between 20180101 and 20181231) a on (auftragsdaten.auftragsnr=a.auftrgsnr)
so ungefähr, die korrekte Syntax und die between-Grenzen müßtest Du noch überprüfen.

Gruß
K-H

P.S.
Zumndest Oracle begrenzt die "in-Menge" auf 2000 Einträge.

Delphi.Narium 8. Feb 2019 22:42

AW: in-Klausel in großen Tabellen
 
Zitat:

Zitat von p80286 (Beitrag 1425122)
Zumndest Oracle begrenzt die "in-Menge" auf 2000 Einträge.

Wenn ich mich recht erinnere aber nur, wenn man sie im SQL auflistet, nicht jedoch als Ergebnismenge einer Abfrage.

SQL-Code:
select * from tabelle where Spalte in ('1','2', ... ,'2000')
mit hier aufgeführen 2000 Werten müsste funktionieren, nicht jedoch
SQL-Code:
select * from tabelle where Spalte in ('1','2', ... ,'2001')
mit hier aufgeführten mehr als 2000 Werten.
SQL-Code:
select * from tabelle where Spalte in (select anderespalte from anderetabelle)
sollte auch noch bei ein paar millionen Datensätzen funktionieren, wenn auch nicht unbedingt sehr schnell.

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:
... where exists (select 1 from ...
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.

Jumpy 11. Feb 2019 07:51

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?

ConnorMcLeod 11. Feb 2019 08:36

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)

p80286 11. Feb 2019 08:56

AW: in-Klausel in großen Tabellen
 
Zitat:

Zitat von Jumpy (Beitrag 1425226)
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?

Für das exists spricht, das es der menschlichen Denke engegenkommt. Ich habe aber die Erfahrung gemacht, das ein Join mit einem check auf NULL wesentlich schneller ist.

Gruß
K-H

Delphi.Narium 11. Feb 2019 09:13

AW: in-Klausel in großen Tabellen
 
Zitat:

Zitat von Jumpy (Beitrag 1425226)
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?

Warum sollte das so sein?

Dort ist eine recht gute Erklärung zu finden: https://stackoverflow.com/questions/...-exists-clause

Zitat:

Zitat von JNK
EXISTS is used to return a boolean value, JOIN returns a whole other table

EXISTS is only used to test if a subquery returns results, and short circuits as soon as it does. JOIN is used to extend a result set by combining it with additional fields from another table to which there is a relation.

In your example, the queries are symantically equivalent.

In general, use EXISTS when:

You don't need to return data from the related table
You have dupes in the related table (JOIN can cause duplicate rows if values are repeated)
You want to check existence (use instead of LEFT OUTER JOIN...NULL condition)

If you have proper indexes, most of the time the EXISTS will perform identically to the JOIN. The exception is on very complicated subqueries, where it is normally quicker to use EXISTS.

Beide Varianten prüfen, ob es zu der Bedingung ein "Gegenstück" gibt. Während Exists nur sagt: "Ja gibt es" bzw. "Nein, gibt es nicht", liefert Join das Gegenstück. Bei großen Datenmengen kann das schon einen großen Unterschied bezüglich des Speicherbedarfes geben.
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.

peterbelow 11. Feb 2019 10:14

AW: in-Klausel in großen Tabellen
 
Zitat:

select * from tabelle
where AuftragNr in (
select AuftragNr from Tabelle where Year(FertigDatum) = 2018 group by AuftragNr)
Wozu das group by in der inneren query? Eventuell funktioniert das hier besser:

Code:
select distinct <feldliste> from tabelle
where exists (
select AuftragNr from Tabelle where Year(FertigDatum) = 2018)
order by auftragnr, arbeitsgang
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
das Resultat von

Code:
select distinct AuftragNr from Tabelle where Year(FertigDatum) = 2018)
ablegen. Diese Query gibt halt deutlich weniger Zeilen zurück als ohne "distinct", ist aber aufwendiger in der Verarbeitung.

freejay 11. Feb 2019 12:56

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:
SELECT * FROM tabelle a
INNER JOIN (
  SELECT DISTINCT AuftragNr FROM Tabelle where Jahr = 2018) i ON a.AuftragNr = i.AuftragNr
ORDER BY auftragnr, arbeitsgang
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:

Code:
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`;
Wenn diese Statements zusammen nicht innerhalb eines Fingeschnippsens durch sind, fresse ich einen Besen... :lol:

PS1: Ich habe nicht alle vorherigen Antworten komplett durchgelesen, vielleicht wiederhole ich auch nur bereits Gesagtes.
PS2: Ich habe die Statements nicht ausprobiert.

freejay 11. Feb 2019 13:16

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...

Delphi.Narium 11. Feb 2019 13:39

AW: in-Klausel in großen Tabellen
 
Die Auftragsnummer wird über
SQL-Code:
concat(waaunr,'-', waaupo)
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.

freejay 11. Feb 2019 13:45

AW: in-Klausel in großen Tabellen
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1425264)
Die Auftragsnummer wird über
SQL-Code:
concat(waaunr,'-', waaupo)
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.

Ok, das habe ich übersehen. Das ist ja aber für die Performance ganz schlecht. Ich würde daher dringend empfehlen, diesen Wert als echtes Tabellenfeld einzuführen. Ja, das ist irgendwie redundant, aber manchmal, wenn's um die Performance geht ist das die einzige wirklich gut funktionierende Lösung.

Delphi.Narium 11. Feb 2019 14:06

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 10:44 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz