Autor Beitrag
hansa
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3079
Erhaltene Danke: 9



BeitragVerfasst: Fr 28.02.03 12:55 
Hi,

ich habe hier etwas zumindest für mich kompliziertes. Zuerst einmal, was ich will : eine simple Rechnung schreiben, und zwar eine vorhandene nur durch Eingabe der entsprechenden Nr. Also brauche ich die Daten des Kunden, Rechnungskopf, Rechnungspositionen und zugehörige Artikel. ohne das kommt wohl keine Rechnung aus. Habe mal so angefangen :

ausblenden Quelltext
1:
RecDS.SelectSQL.Text := 'SELECT R.*,A.ID,A.NR,A.BEZ,A.BEZ2 FROM REC8 R JOIN ART8 A ON (A.ID = R.ID_ART)                          ' WHERE R.ID_RECKOPF = ' + ReNrEdit.Text + ' AND R.MENGE <> 0 ';					


In ReNrEdit.Text muß hier allerdings noch die ID des RecKopf eingegeben werden, nicht die Nr. selber und der Kunde ist egal. Dieses Programm fischt mir auch schön alle Rechnungspositionen samt Menge und passenden Artikeln aus der DB. Jetzt muß ich logischerweise aber das ganze etwas modifizieren:
    1. über die Rechn.-Nr wird der Rechnungskopf gefunden
    2. Mit ID_KUNDE der entsprechende Kunde
    3. dann gehts ungefähr so weiter wie oben


Da das Select-Statement schon jetzt nicht gerade ganz mickrig ist, frage ich mich, ob das der richtige Weg ist. Ich müßte ja um alles in ein DataSet reinzukriegen zusätzlich noch einen Join über die Tables Kunde und RecKopf machen. Oder soll ich das da so lassen und noch 2 Datasets für RecKopf und Kunde da drauf packen. Dabei braucht ja nur jeweils ein einzelner Datensatz zurückgeliefert werden. Macht ein Join da überhaupt Sinn ? Alleine schon wegen der Lesbarkeit ?

_________________
Gruß
Hansa
neojones
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1206
Erhaltene Danke: 1



BeitragVerfasst: Fr 28.02.03 13:30 
Jep,

meine Erfahrung: So lange es geht, sollte man alles in ein SQL-Statement packen. So Master-Detail-Biehungen oder Abfragen über weitere Statements sind nämlich noch unübersichtlicher, da in aller Regel nicht ganz so eindeutig erschließbar.

Ich hab vor ein paar Tagen eine Abfrage geschrieben, die sich über 8 Tabellen bezog, alles über JOINs und zusätzlich wurde eine Tabelle 4 mal rekursiv über einen JOIN eingebunden (Also selbe Tabelle in mehreren JOINs, geht dann halt nur über Alias-Namen).

Also mein Tipp: SQL ist logisch und strukturiert, bei Master-Details denkt man nur, sie wären logisch...

Viele Grüße,

Matthias

_________________
Ha! Es compiliert! Wir können ausliefern!
hansa Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3079
Erhaltene Danke: 9



BeitragVerfasst: Fr 28.02.03 13:40 
Und was hälst Du davon, es so zu lassen und noch 2 Datasets dazu zu nehmen ? Das probiere ich jetzt gerade. Gehen tuts aber noch nicht. :mrgreen: Was mich etwas stört, ist halt die Tatsache wegen EINES Kunden und EINES Rechnungskopfes den Join so aufzublähen. Was ist eigentlich mit der Performance ?

_________________
Gruß
Hansa
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: Fr 28.02.03 13:59 
Hi!

Genau das ist der Haken bei Join: Die Performance. Der Server an sich eiert sich tot bei einem join (BTW: in Interbase lohnt sich immer eine Umwandlung in eine Stored Procedure). Anschliessend werden alle möglichen Daten, die man eigentlich nur einmal benötigt, x mal, im Worst Case Millionen mal gesendet. Das ist doch überhaupt nicht nötig und bei größeren Einsatzgebieten der absolute Anwendungstod!

Nimm dir lieber mehrere Datasets und verknüpfe sie. Auch wenn manche Probleme mit Master-Detail-Beziehungen haben, weil sie nicht übersichtlich sind, sie sind auf alle Fälle die performantere (und professionellere) Variante.

Cu,
Udontknow
Cashels
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 167



BeitragVerfasst: Fr 28.02.03 14:46 
Udontknow hat folgendes geschrieben:
Anschliessend werden alle möglichen Daten, die man eigentlich nur einmal benötigt, x mal, im Worst Case Millionen mal gesendet....
Nimm dir lieber mehrere Datasets und verknüpfe sie. Auch wenn manche Probleme mit Master-Detail-Beziehungen haben, weil sie nicht übersichtlich sind, sie sind auf alle Fälle die performantere (und professionellere) Variante.


Hi,
ist das nicht eher umgekehrt. Bei Datasets und Master-Detail Verknüpfungen im Delphi wird alles auf dem Client abgehandelt, was überhaupt nicht im Sinne einer C/S Anwendung ist. Ich versteh nicht ganz wieso im Worst Case Millionen Daten gesendet werden im Falle eines Joins? Der SQL Server filtert dir doch grad mit der SQL-Anweisung die Daten raus, die der Client braucht.

Gruss,
Tom
neojones
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1206
Erhaltene Danke: 1



BeitragVerfasst: Fr 28.02.03 15:40 
@Udontknow: nahezu alle Datenbanken unterstützen server-sided-keys!!!

Bei einem JOIN wird NUR die Ergebnismenge zurückgeliefert.

Es gibt aber maßgebende Unterschiede (Gilt übrigens für alle Datenbanken):

Führt man ein Statement in der Form

ausblenden Quelltext
1:
2:
3:
4:
5:
SELECT tabelle1.feld1, tabelle2.feld2

FROM tabelle1, tabelle2

WHERE tabelle1.feld1=tabelle2.feld2


aus, dann wird zunächst das kartesiche Produkt abgebildet und aus dem kartesichen Produkt die Ergebnismenge erstellt => gigantisches Datenaufkommen.

Führt man die Abfrage in der Form

ausblenden Quelltext
1:
2:
3:
4:
5:
SELECT tabelle1.feld1, tabelle2.feld2

FROM tabelle1

LEFT JOIN tabelle2 ON tabelle1.feld1=tabelle2.feld2


aus, dann wird sofort die Ergebnismenge geliefert.

JOINS sind aus PRINZIP schneller als JEDE Master-Detail-Beziehung (Verwett ich meinen Hintern drauf...). Auch, wenn nur ein einziger Datensatz ermittelt werden soll. Und genau dafür wurden ja JOINs eingeführt.

Voraussetzung für gute Performance ist natürlich, dass die Indizes korrekt gesetzt sind.

Und dann noch die Frage: Was soll professioneller daran sein, mehrere Komponenten zu verwenden, wenn eine einzige genügt?

Viele Grüße,

Matthias

_________________
Ha! Es compiliert! Wir können ausliefern!
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: Fr 28.02.03 15:52 
@Neojones:

Auch wenn unter Verwendung von Join sofort die Ergebnismenge geliefert wird, so ist der Knackpunkt noch immer der, daß für jeden Positionssatz Kundendaten und Rechnungskopf mitgeschickt werden. Bei 20 Positionen verschickst du 20 mal dieselben Werte: Name, Vorname, Strasse, PLZ, Ort, Auftragsnr, Datum. Der Netzwerkadmin wird sich freuen.
Das ist absolut zu vermeiden, und wenn du soetwas bei einer Operation, in der du aus 10 Tabellen Hunderttausende Datensätze aufbereitest (z.B. Lohnabrechnung), anwendest, bekommst du ein Problem...

Meine Erfahrung mit JOIN ist (zumindest unter IB) : Finger weg, wenn Daten aus einer anderen Tabelle benötigt werden, lieber per Stored Procedure zusammensuchen.

Cu,
Udontknow


Zuletzt bearbeitet von Udontknow am Fr 28.02.03 16:01, insgesamt 1-mal bearbeitet
neojones
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1206
Erhaltene Danke: 1



BeitragVerfasst: Fr 28.02.03 15:55 
Tja, hier beginnt die Gradwanderung zwischen Netzwerkperformance und Rechnerperformance ....

Der Netzwerktraffic wird mit Sicherheit steigen, wenn man Joins auf diese Art anwendet. Auf der anderen Seite wird der Server erheblich entlastet.

Jetzt kommts maßgeblich auf die Anzahl der User und die verwendete Datenbank an.

_________________
Ha! Es compiliert! Wir können ausliefern!
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: Fr 28.02.03 16:27 
Hallo nochmal,

ich habe mich gerade noch einmal drangesetzt und überprüft, ob es stimmt, was ich da so schreibe... :wink:

Ich habe mir eine DB für eine Fuhrpark-Verwaltung genommen und dort mir eine Bestellung per JOIN gezogen:

ausblenden Quelltext
1:
2:
3:
select * from Bestellung A left join Artikel B left join Lieferanten C
on A.best_nr=B.best_nr on A.lieferant_nr =C.liefer_lfdnr
where A.best_nr='1999-0001'


Es kam ein einziger Datensatz heraus, was auch richtig ist.

Der Server war 26 Sekunden damit beschäftigt, die Auswertung ergab, daß er über 1.000.000 unindizierte Reads durchführen musste, obwohl alle oben erwähnten Felder indiziert sind...

Das Statement aufgespalten in drei separate (entspräche dann dem Master/Detail-Verfahren):

ausblenden Quelltext
1:
2:
3:
select * from Bestellung A where A.best_nr="1999-0001"
select * from Artikel B where B.best_nr="1999-0001"
select * from Lieferanten C where C.liefer_lfdnr ="16"


Diese drei Statements brauchten nur insgesamt 40ms...

Habe ich vielleicht etwas falsch gemacht in der Anwendung des joins?

Cu,
Udontknow
hansa Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3079
Erhaltene Danke: 9



BeitragVerfasst: Fr 28.02.03 19:25 
Huch, was ist denn hier los? Hab anscheinend wieder mal eine intensive Diskussion ausgelöst. :P Aber die ist berechtigt. Jetzt guck ich aber nicht genau, wer was gesagt hat. Also ich schreibe grundsätzlich alles für den Netzwerk-Einsatz. Wenn es da zufriedenstellend läuft, sollte es bei einem Stand-Alone-System wohl 3-mal so gut sein. 8) Ausnahme : Taschenrechner usw. Aber eine Frage scheint doch elementar zu sein :
Zitat:
kartesisches Produkt ?
Das ist das Stichwort :!: Ist ein Join ein solches oder nicht? Ich dachte bei SQL sei das etwas anders zu sehen. Desweiteren habe ich das Stichwort Index in dem Thread noch nicht entdeckt.

Wenn hier sonst noch jemand etwas weiß : her damit. Ansonsten muß ich eine eigene Testreihe aufbauen.

_________________
Gruß
Hansa
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: Fr 28.02.03 20:12 
Dafür aber das Stichwort "Indiziert" respektive "unindiziert"... :wink:
hansa Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3079
Erhaltene Danke: 9



BeitragVerfasst: Fr 28.02.03 20:22 
In dem Thread hier ?

Zitat:
Keine Beiträge entsprechen Deinen Kriterien.


In dem Zusammenhang taucht das jedenfalls nicht auf. Faschingsscherz ? :mrgreen:

_________________
Gruß
Hansa
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: Fr 28.02.03 20:35 
Zitat von meinem vorletzten Post:

Der Server war 26 Sekunden damit beschäftigt, die Auswertung ergab, daß er über 1.000.000 unindizierte Reads durchführen musste, obwohl alle oben erwähnten Felder indiziert sind...

Die Suchfunktion klappt wohl immer noch nicht recht...

Cu,
Udontknow
hansa Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3079
Erhaltene Danke: 9



BeitragVerfasst: Fr 28.02.03 21:26 
Ah ja :

Zitat:
1.000.000 unindizierte
So ein Zungenbrecher aber auch, kein Wunder, wenn das die Suchfunktion nicht unterstützt. :shock: :mrgreen: Komme an der Testreihe bei mir wohl nicht vorbei, dann such ich morgen mal so ein paar Mio. Records und teste das ganze mal selber.

Trotzdem sind die Beiträge hier sehr widersprüchlich.

Zitat:
Also mein Tipp: SQL ist logisch und strukturiert, bei Master-Details denkt man nur, sie wären logisch...

Gibt es ein gewaltiges Performance-Problem nützt mir die Logik nix mehr. Master Detail ist das doch sowieso nicht, oder? Bei Udontknow stellt der Unterschied ungefähr einen Faktor 1000 dar. Das ist schon heftig und nicht zu entschuldigen. Man stelle sich das mal bei 1000 Workstations vor :!: Ich bin wohl einer der letzten, der Nanosekunden nachrennt, aber 26 Sek., da renne ich vielleicht 200 m. Wenn ich dieses Ergebnis sehe, schätze ich, daß tatsächlich ein kartesisches Produkt gebildet wird. Wie soll das denn auch anders gehen. Also ein mehrdimensionales Array, hammerhart. Wenn das dann in die Knie geht wäre kein Wunder.

Bei meinem Join suche ich ja nur die zur Rechnungspos. gehörigen Artikel. Was haben da die Kunden mit zu tun? Höchstens doch mit der EINEN Rechn.-Nr.

_________________
Gruß
Hansa
neojones
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1206
Erhaltene Danke: 1



BeitragVerfasst: Sa 01.03.03 12:14 
@UdontKnow:

ausblenden Quelltext
1:
2:
3:
select * from Bestellung A left join Artikel B left join Lieferanten C 
on A.best_nr=B.best_nr on A.lieferant_nr =C.liefer_lfdnr 
where A.best_nr='1999-0001'


Ist falsch. Da fehlt die ON-Condition im Joining der Tabelle Artikel B ...

Kein Wunder, dass Du dann so viele Datensätze rausbekommst!!!

@hansa:

Das kartesiche Produkt ist die Verbindung aus jedem Datensatz der einen Tabelle mit jedem Datensatz der anderen Tabelle. Wenn Du zwei Tabellen hast, die jeweils Tupel mit den Werten "A", "B" und "C" enthalten, dann ist das kartesische Produkt

A A
A B
A C
B A
B B
B C
C A
C B
C C

Wenn Du also einen Equi-Join (WHERE tabelle1.feld1=tabelle2.feld2) ausführst, wird zuerst temporär die obige Datenmenge ausgeführt und erst aus dieser Datenmenge das Ergebnis

A A
B B
C C

ermittelt.

Machst Dus mit einem JOIN, wird sofort das Ergebnis ermittelt.

Kartesiche Produkte braucht man eigentlich nur ganz selten und die sind extrem rechnerintensiv (Übrigens bildet auch der Join, wie ihn UDontknow geschrieben hat zunächst ein kartesisches Produkt ab!).

Viele Grüße,

Matthias

_________________
Ha! Es compiliert! Wir können ausliefern!
Cashels
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 167



BeitragVerfasst: Sa 01.03.03 14:48 
Hallo,

also diese Diskussion ließ mir die letzten Stunden auch keine Ruhe, und ich habe mal auch grad eine kleine Datenbank gebasttelt, mit 4 Tabellen und jeweils zwischen 100.000 und 120.000 Records pro Tabelle. Also schon ein ganzes Stück Holz. Folgende Struktur:

Tabelle Kunden: ID int, Name varchar(30), Vorname varchar(30)
Tabelle Artikel: ID int, Artikel varchar(30), Preis float
Tabelle Rechnungen: ID int, Kunde int, Datum datetime
Tabelle Rechnungsposten: ID int, Artikel int, Rechnung int, Anzahl float

Wie ich die Tabellen verknüpft hab dürfte eigentlich klar sein. über Rechnungen.Kunde wird auf den Kunden referenziert, unter Rechnungsposten.Rechnung auf die Rechnungsnummer.

Jetzt hab ich folgende view erstellt:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
create view vwRechnungen as
SELECT     Kunden.ID AS KundenID, Kunden.Name, Kunden.Vorname, Rechnungen.ID AS RechnungID, 
                      Rechnungen.Datum, Rechnungsposten.Anzahl, Artikel.ID AS ArtikelID, Artikel.Artikel, Artikel.Preis
FROM         Kunden RIGHT OUTER JOIN
                      Rechnungen ON Kunden.ID = Rechnungen.Kunde LEFT OUTER JOIN
                      Rechnungsposten INNER JOIN
                      Artikel ON Rechnungsposten.Artikel = Artikel.ID ON Rechnungen.ID = Rechnungsposten.Rechnung
ORDER BY Kunden.Name, Kunden.Vorname, Rechnungen.ID


Soviel also zur Datenbankstruktur. Wie in einem oben genannten Beispiel möchte ich jetzt z.B. alle Rechnungen mit Posteninformation haben, von einem gewissen Kunden. Also starte ich folgende Abfrage:
ausblenden Quelltext
1:
select * from vwRechnungen where Vorname='Tom'    /" nur als Beispiel. Kann natürlich auch über die KundenID erfolgen */					


Jetzt zum interessanten Ergebnis, was dem oben genannten in meinem System widerspricht... 0,15 Sekunden.

Ganz wichtig noch:
Datenbanksystem: Micorsoft SQL Server 2000
auf Athlon 1000 MHz und 512 MB Ram.

Damit wäre gezeigt daß die Sache mit den Joins sehr datenbankspezifisch ist.

Besten Gruß,
Tom
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: So 02.03.03 12:02 
neojones hat folgendes geschrieben:
@UdontKnow:

Ist falsch. Da fehlt die ON-Condition im Joining der Tabelle Artikel B ...

Kein Wunder, dass Du dann so viele Datensätze rausbekommst!!!



Ich habe nur einen DS rausbekommen. Er hat aber 1.000.000 Reads gemacht.

Und wieso fehlt eine ON-Condition? Für das Verknüpfen von 3 Tabellen brauche ich doch nur 2 Bedingungen, und die Bedingung für die Tabelle Artikel war doch gesetzt:

select * from Bestellung A left join Artikel B left join Lieferanten C
on A.best_nr=B.best_nr on A.lieferant_nr =C.liefer_lfdnr
where A.best_nr='1999-0001'

Wenn das falsch ist, wie sieht denn dann das richtige Statement aus?

Cu,
Udontknow
hansa Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3079
Erhaltene Danke: 9



BeitragVerfasst: So 02.03.03 12:17 
Cashels hat folgendes geschrieben:
Damit wäre gezeigt daß die Sache mit den Joins sehr datenbankspezifisch ist.


Wie kommst Du zu dem Schluß ? :mrgreen: Ich habe Dein Beispiel mal für mich angepaßt. Das liefert genau 55 Ergebnisse. Aber ich habe den RIGHT, LEFT, OUTER, INNER JOIN verändert und einfach nur JOIN geschrieben. Geht ziemlich flott. Kann mir mal jemand erklären, wozu das gut sein soll ? Es geht doch nur darum NULL - Werte mitzuschleppen oder eben nicht, oder doch :?:

Außerdem würde mich mal interessieren, wie sich das ganze auswirkt, wenn man die Reihenfolge der JOINs verändert. Was wäre denn dann ?

_________________
Gruß
Hansa
neojones
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1206
Erhaltene Danke: 1



BeitragVerfasst: Mo 03.03.03 10:03 
@UdontKnow:

ausblenden Quelltext
1:
left join Artikel B left join Lieferanten					


Da stehen zwei Joins direkt nacheinander! Bereits der erste braucht eine ON-Condition.

ausblenden Quelltext
1:
LEFT JOIN Artikel B ON [...] LEFT JOIN Lieferanten IN [...]					


Viele Grüße,

Matthias

_________________
Ha! Es compiliert! Wir können ausliefern!
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: Mo 03.03.03 10:12 
Ahso! Danke, jetzt ist es also 2 Sekunden schnell.

ausblenden Quelltext
1:
2:
3:
select * from Artikel B left join Bestellung A on A.best_nr=B.best_nr
left join Lieferanten C on C.liefer_lfdnr = A.lieferant_nr
where A.best_nr='1999-0001'


Das ist aber immer noch 50 (Editiert, jaja, Mathe...) mal langsamer als die einzelnen Statements zusammen (zumindest bei mir unter IB)...

Cu, :)
Udontknow