SQL-Abfrage ohne first / last über mehrere Tabellen

stefaktiv

Grünschnabel
Hallo ihr,

nachdem ich meine Access-Datenbank zum MS SQL Server 2008 Express migriert habe, funktioniert leider der SQL-Befehl "First" / "Last" nicht mehr. Ich habe jetzt verzweifelt überall im Internet nach einer Lösung gesucht, bin aber nicht wirklich weitergekommen. Irgendwie müßte es mit einem "Subselect" oder "Subquery" funktionieren - aber die bereits ausprobierten Codes gingen nicht.

Zur Problemstellung:

Ich habe eine Tabelle Personen, eine Tabelle PersEmail und eine Tabelle Emailadressen. In erster sind klar alle Personen, in zweiter eine M:N-Verknüpfung zwischen Personen und Emailadressen und in dritter die Emailadressen selbst.

Bsp (vereinfacht):
----
[Personen]
[Felder: Pers_ID / Pers_Nachname / Pers_Vorname]
1 / Meier / Karl
2 / Huber / Heinz
3 / Schmidt / Klaus
4 / Meier / Claudia
5 / Müller / Reiner
----
[PersEmail]
[Felder: PersEmail_ID / Pers_ID / Email_ID / Letzte_Änderung]
1 / 1 / 1 / 01.01.2008
2 / 3 / 2 / 20.10.2008
3 / 1 / 3 / 30.10.2008
4 / 2 / 4 / 21.10.2008
5 / 4 / 1 / 02.01.2008
----
[Emailadressen]
[Felder: Email_ID / Email_Adresse]
1 / familie.meier@gmx.de
2 / klaus.schmidt@web.de
3 / karl.meier@yahoo.com
4 / heinz.huber@hotmail.com
----

Was ich brauche ist die aktuelle Emailadresse der Person. Bis dato war das ganze nicht so schwierig. Alle drei Tabellen waren miteinander verknüpft, die M:N-Tabelle nach dem Datum der letzten Änderung der jeweiligen Verknüpfung absteigend sortiert und dann habe ich einfach mit "Erster Wert" die aktuellste Emailadresse rausgesucht.

Bsp. gewünschtes Trefferergebnis:
[Felder: Pers_ID / Pers_Nachname / Pers_ Vorname / Email_ID / Email_Adresse / Letzte_Änderung]
1 / Meier / Karl / 3 / karl.meier@yahoo.com / 30.10.2008
2 / Huber / Heinz / 4 / heinz.huber@hotmail.com / 21.10.2008
3 / Schmidt / Klaus / 2 / klaus.schmidt@web.de / 20.10.2008
4 / Meier / Claudia / 1 / familie.meier@gmx.de / 02.01.2008

----

Mit dem SQL-Server funktioniert jetzt "First" und "Last" leider nicht mehr. Ich habe jetzt schon diverse Lösungen mit "Top 1" durch - allerdings bekomme ich dann immer nur die erste Person angezeigt. Alle Versuche das "TOP 1" irgendwo in den Code zu bringen um zu zeigen, dass ich nur bei der Emailadresse den ersten Wert angezeigt bekommen will half leider nicht.

Wenn man mit einer Gruppierung arbeitet, dann kann man zwar bei "Letzte_Änderung" mit Max() den höchsten Wert herausfinden - allerdings muss man dann Email_ID auch mit in die Gruppierung reinnehmen. Nimmt man hier auch Max(), dann sucht er alphabetisch den höchsten oder niedrigsten Wert heraus (bei Min()). Wählt man bei der Email_ID hingegen Group by, dann tauchen im Trefferergebnis wieder alle Emailadressen der Person auf.

Es wäre wirklich supergenial, wenn irgendjemand hier eine Lösung wüßte. An sich kann es keine große Sache sein.

Stefan

Hier der (vereinfachte) Access-Code, der wunderbar funktionierte...:

Code:
SELECT   P.Pers_ID, First(P.Pers_Nachname) AS ErsterWertvonPers_Nachname, 
         First(P.Pers_Vorname) AS ErsterWertvonPers_Vorname, 
         First(E.Email_Letzte_Änderung) AS ErsterWertvonEmail_Letzte_Änderung, 
         First(E.Email_ID) AS ErsterWertvonEmail_ID, 
         First(E.Email_Adresse) AS ErsterWertvonEmail_Adresse 
FROM     (PersEmail AS PE 
          RIGHT JOIN Personen AS P 
          ON PE.Pers_ID = P.Pers_ID) 
         LEFT JOIN Emailadressen AS E 
         ON PE.Email_ID = E.Email_ID 
GROUP BY P.Pers_ID 
ORDER BY P.Pers_ID, First(E.Email_Letzte_Änderung) DESC;
 
Zuletzt bearbeitet:
Hi,

ich habe mir Deinen code nicht angesehen aber was Du auf jeden Fall benutzen solltest sind die analytic funtions:
http://www.sqlbooks.ru/readarticle.aspx?part=02&file=sql200523

mit rank() b.z.w dense_rank() kannst du werten (auf zeilenbasis) wertigkeiten geben, sortieren und ausgeben lassen. Window bedeutet dabei das Du Partitionen bildest.
Diese functions sehen auf den ersten Blick kompliziert aus, brauchen etwas Einarbeitung, sind aber GOLD in der Benutzung und optimieren Abfragen/Möglichkeiten und entwicklungszeiten um Faktoren :o)

Ich hoffe Dir geholfen zu haben.

LG
 
Zuletzt bearbeitet:
ich fang mal klein an also das mit dem subselect würde so gehen:
SQL:
SELECT P.Pers_ID , (Select top 1 g.Nachname From Personen g where g.Pers_ID = P.PersID)  as ErsterWertvonNachnahme
FROM (PersEmail AS PE
RIGHT JOIN Personen AS P
ON PE.Pers_ID = P.Pers_ID)
LEFT JOIN Emailadressen AS E
ON PE.Email_ID = E.Email_ID
GROUP BY P.Pers_ID
ORDER BY P.Pers_ID, First(E.Email_Letzte_Änderung) DESC;

So nun zu deinem SQL allgemein:
Das was dir die Access QBE gebastelt hat ist definitiv kein performanter SQL und ich kenne genug Leute die wegen so etwas gesteinigt würden.

Versuch den SQL einfach mal anders aufzubauen und zwar ohne das kostenintensive Right Join,
des weiteren brauchst du eigentlich auch kein First wenn du richtig gruppierst und joinst

Grüsse bb

[edit]

SQL:
Select 
Pers_ID , Pers_Nachname , Pers_ Vorname , Email_ID , Email_Adresse , 
(Select Max(Letzte_Änderung) From PersEmail where PersEmail.pers_ID = Personen.Pers_ID and PersEmail.Email_ID =  Personen.Email_ID 
) as
Letzte_Änderung

From Personen
Inner Join Emailadressen on Personen.PersID = Emailadressen.PersID
 
Zuletzt bearbeitet von einem Moderator:
Hallo brainbyte,

erstmal danke für deinen Code. Der MS SQL Server Express ändert ihn mir wie folgt:

Code:
1. SELECT     
2. p.Pers_ID, p.Pers_Nachname, p.Pers_Vorname, e.Email_ID, e.Email_Adresse,
3. (SELECT MAX(PersEmail_Letzte_Änderung) AS MaxvonPersEmail_Letzte_Änderung
4. FROM dbo.PersEmail AS pe
5. WHERE (Pers_ID = p.Pers_ID) AND (Email_ID = e.Email_ID)) AS Letzte_Änderung
6. FROM dbo.Personen AS p 
7. INNER JOIN dbo.Emailadressen AS e 
8. ON p.Pers_ID = e.Pers_ID


Speichern kann ich ihn aber nicht, weil es den letzten Ausdruck e.Pers_ID nicht gibt. In der Tabelle Emailadressen gäbe es höchstens Email_ID, aber die Email_ID und die Pers_ID hängen ja gerade nicht direkt miteinander zusammen.

Der Versuch irgendwie die Verknüpfungstabelle PersEmail hier noch mit ins Boot zu bringen mißlangen. Dort gäbe es natürlich sowohl Pers_ID als auch Email_ID.

Der erste Code funktioniert auch nicht. Ganz am Ende steht da noch First() - das kennt er natürlich nicht. Lass ich es weg, dann bringt er trotzdem alle Emailadressen der Personen.

Wahnsinn - eigentlich ist das nur ne Kleinigkeit - es kann doch nicht sein, dass niemand hier ne Lösung dazu kennt...



Fällt dir noch ein, was man ggf. noch ändern muss?

Grüße

Stefan
 
Zuletzt bearbeitet:
Hallo brainbyte,

erstmal danke für deinen Code. Der MS SQL Server Express ändert ihn mir wie folgt:

Das ist schon mal ganz schlecht ! Ich würde das nicht zulassen.
Der Versuch irgendwie die Verknüpfungstabelle PersEmail hier noch mit ins Boot zu bringen mißlangen. Dort gäbe es natürlich sowohl Pers_ID als auch Email_ID.
Bei mir hat es geklappt ? Was hast du denn versucht ?

Wahnsinn - eigentlich ist das nur ne Kleinigkeit - es kann doch nicht sein, dass niemand hier ne Lösung dazu kennt...
Ist es auch ich habe für die Lösungen weniger als einige Minuten verwendet ohne sie testen zu können, allerdings hätte ich auch von dir etwas mehr transformationsvermögen erwartet ;-) ( nicht persönlich nehmen )

Fällt dir noch ein, was man ggf. noch ändern muss?
Klar look at this:

SQL:
      SELECT P.Pers_ID, Pers_Nachname , Pers_Vorname ,   e.Email_ID , Email_Adresse ,
      (SELECT Max(Letzte_Änderung) FROM PersEmail WHERE PersEmail.pers_ID = P.Pers_ID AND PersEmail.PersEmail_ID =  e.Email_ID
      ) AS
      Letzte_Änderung
      FROM Personen P
      Inner JOIN PersEmail pe on P.Pers_ID = pe.Pers_id
      Inner JOIN Emailadressen e ON Pe.PersEmail_ID = e.Email_ID
      group by   P.Pers_ID, Pers_Nachname , Pers_Vorname ,  e.Email_ID , Email_Adresse

Wenn du auch die "Jungs" ohne Email Adresse ausgeben möchtest so musst du statt inner Left joins machen

Grüsse bb
 
Zurück