In einer SQL Übungsaufgabe soll ich die Zeile ausgeben, die in einer Spalte den fünftgrößten Wert hat. Als Server läuft Oracle Database.
Leider unterstützt der Server nicht alle Befehle, die man auch von MySQL kennt und somit gibt es auch kein Limit-Befehl.
Die Aufgabe selbst soll mit einer Unterabfrage gelöst werden. Ich steh da irgendwie auf dem Schlauch. Kann da vielleicht jemand ein Tipp geben?
In einer SQL Übungsaufgabe soll ich die Zeile ausgeben, die in einer Spalte den fünftgrößten Wert hat. Als Server läuft Oracle Database.
Leider unterstützt der Server nicht alle Befehle, die man auch von MySQL kennt und somit gibt es auch kein Limit-Befehl.
Die Aufgabe selbst soll mit einer Unterabfrage gelöst werden. Ich steh da irgendwie auf dem Schlauch. Kann da vielleicht jemand ein Tipp geben?
...
Ich hätte da auch aufm Schlauch gestanden, aber weil mich das gerade interessiert hat, hab ich mal gegooglet und bin erst auf diesen Blogeintrag und später auf diesen Artikel von Oracle gestoßen. Solltest du dir nicht gleich die Lösung auf dem Silbertablett präsentieren lassen wollen: Der Trick heißt wohl ROWNUM. So wie ich das verstehe, ginge das dann so:
Sollte eigentlich auch ohne solche Spezialkonstrukte gehen (kann ich jetzt für Oracle natürlich nicht garantieren).
Überleg mal, was du suchst, ist die Zeile x, wo die Anzahl der Zeilen, bei denen ein bestimmter Wert kleiner ist als der der Zeile x, 4 ist.
Kleiner Hint: Die Anzahl zählt man mit...
COUNT
Mal die Lösung, die ich spontan auf meiner WebSeiten-Datenbank gemacht habe (Tabelle heißt hier "page", die Spalte, nach der verglichen wird, ist die page_id):
SELECT * FROM page p WHERE (SELECT count(*) FROM page q WHERE q.page_id < p.page_id) = 4;
(Wie schon geschrieben: Selektiere die Zeile, wo die Anzahl der Zeilen, bei denen die page_id kleienr ist, gleich 4 ist, was dann natürlich die 5. Zeile ist)
Ich glaube, du irrst dich. Meine Lösung gibt die Zeile aus, für die es 4 andere Zeilen gibt, wo der Wert einer bestimmten Spalte kleiner ist. Also ist das die Zeile...
Zitat
die in einer Spalte den fünftgrößten Wert hat
...
Ich sehe nicht, warum die Sortierung eine Rolle spielen sollte. Es geht um den fünft-größten Wert, ganz egal ob der in Zeile 5 oder 123 steht.
Edit/Hinweis: Ich glaube, du hast was nicht verstanden: PAGE_ID ist ein BEISPIEL. Es geht bei meinem Query NICHT darum, die ID zu selektieren. Statt Page_ID könnte da auch "Preis", "Hausnummer", "Datum" oder sonstwas stehen. Das ist einfach die Spalte, wo der Wert steht. Mein Query hängt NICHT von einem Schlüssel ab. Ich hab nur fürs Beispiel auf die Schnelle eine Spalte genommen, wo numerische Werte standen. Ich hätte da auch "date" schreiben können.
Mein Beispiel selektiert also die Zeile mit der fünftgrößten page_id. Aber natürlich kann man damit auch die Zeile mit der fünftgrößten Körpergröße selektieren. Oder der Nasenlänge. oder sonstwas :-)
Das sollte für große Datenbanken schneller sein, wenn ich richtig liege, ist aber weniger elegant, da die Kolumnen explizit angegeben werden müssen. Was es macht ist folgendes:
1. Wähle alles aus tab und sortiere absteigend nach coln.
2. Wähle die ersten 5 Reihen.
3. Wähle die 5. Reihe.
Jo, die Abschätzung ist rein theoretisch korrekt - aber rein praktisch gesehen relativ sinnfrei, da in modernen Datenbanken viele Optimierungen ablaufen. Ohne genaue Kenntnis der Datenbank dürfte es schwer sein, realistisch abzuschätzen, was da wirklich rauskommt. Dass meine Lösung aber aller Wahrscheinlichkeit nach nicht die performanteste sein dürfte, ist wohl jedem klar - dafür ist sie halt eher Datenbank-unabhängig :-)
Ich würde sie nicht als sinnfrei bezeichnen. Dass Optimierungen durchgeführt werden ist Fakt, ob jedoch eine Optimierung in dem konkreten Fall stattfindet, so dass die oberflächlich festgestellte O(N^2)-Abschätzung falsch ist, ist alles andere als Fakt und kann ohne genaue Kenntnisse über die Implementierung genauso wenig ausgesagt werden, wie, dass O(N^2) absolut korrekt ist. In jedem Fall ist es eine gute Möglichkeit um mehrere Algorithmen schnell vergleichen zu können, falls es überhaupt auf die Performance ankommt. Genaue (lies: konkrete) Aussagen kann man sowieso nur durch Profiling treffen.
Jo, die Abschätzung ist rein theoretisch korrekt - aber rein praktisch gesehen relativ sinnfrei, da in modernen Datenbanken viele Optimierungen ablaufen. Ohne genaue Kenntnis der Datenbank dürfte es schwer sein, realistisch abzuschätzen, was da wirklich rauskommt. Dass meine Lösung aber aller Wahrscheinlichkeit nach nicht die performanteste sein dürfte, ist wohl jedem klar - dafür ist sie halt eher Datenbank-unabhängig :-)
...
Deine ist weder die performanteste noch die Korrekte, da sie überhaupt keinen Wert zurückliefert
Das kann schonmal nicht funktionieren, weil zuerst die inneren Abfragen bearbeitet werden und was soll denn da rauskommen, da er in der inneren auch die äußere benötigt, die noch nicht existiert?
Ein weiteres Problem ist rownum, da rownum nicht die Nummerierung der ausgegebenen Zeilen anzeigt, sondern die Nummerierung, die intern in der Datenbank verwendet wird. (Zumindest scheint das bei Oracle so zu sein) Meine Zeile, die ich ausgeben soll, hat nämlich den Wert 52 und nicht 5.
Auf die Idee mit rownum kam ich auch schon aber es klappt einfach nicht.
Also ich habe beide Lösungen mit Oracle 10g getestet und beide funktionieren, wobei ich bei Irians noch das '<' durch '>' ersetzen musste, da sonst der fünftkleinste Wert, statt des fünftgrößten ausgegeben wurde. ^^
Und dead_orcs Code (edit: so wie Gekigangers) wird aus dem Grund nicht funktionieren, weil ROWNUM in dem Kontext nicht auf Gleichheit geprüft werden kann, steht auch in dem Oracle-Link, den er gepostet hat.
Deine ist weder die performanteste noch die Korrekte, da sie überhaupt keinen Wert zurückliefert
...
Das ist, kurz gesagt, Unsinn. Natürlich habe mein Query getestet und das Ergebnis war völlig korrekt. Du argumentierst also quasi an der Realität vorbei...
Zitat
Das kann schonmal nicht funktionieren, weil zuerst die inneren Abfragen bearbeitet werden und was soll denn da rauskommen, da er in der inneren auch die äußere benötigt, die noch nicht existiert?
...
Du solltest dein Wissen über SQL bitte nochmal etwas aufbessern, bevor du solche Beinahe-Wahrheiten verkündest. Anders gesagt: Du irrst dich. Such mal nach "correlated subquery".
Zitat
Also ich habe beide Lösungen mit Oracle 10g getestet und beide funktionieren, wobei ich bei Irians noch das '<' durch '>' ersetzen musste, da sonst der fünftkleinste Wert, statt des fünftgrößten ausgegeben wurde. ^^
...
Groß... Klein... Details! Anders gesagt: Ups, stimmt natürlich :-)
Zitat
Dubletten entfernen nicht vergessen.
...
Hier sehe ich sogar einen Vorteil meines Ansatzes, der würde ALLE Zeilen ausgeben, die den fünftgrößten Wert beinhalten. Praktisch :-)
(Edit: Ginge natürlich andersrum auch, einfach höchsten Wert suchen und dann alle Zeilen damit ausgeben, vermutlich sogar schneller... Wie gesagt, dieses innere Query hier dürfte kaum die schnellste Lösung sein, da müßte man sich die Logs mal anschauen).
Geändert von Irian (21.11.2009 um 16:51 Uhr)
Grund: Typo gefixt
Das ist, kurz gesagt, Unsinn. Natürlich habe mein Query getestet und das Ergebnis war völlig korrekt. Du argumentierst also quasi an der Realität vorbei...
...
aha. Dann redet der SQL Developer von Oracle an der Realität vorbei?
Ich sage hier nur, was ich hier sehe und rownum hat den Wert 52, bei der Zeile, die ich ausgeben will.
Zeig deinen Code und ich zeige dir wo dein Fehler ist.
...
select *
from studienleistung
where ects is not null
order by ects desc;
Das ist jetzt die Innere. Wenn ich jetzt select *, rownum schreibe, meldet er sofort einen Fehler.
Die äußere kann ich machen, was ich will. Nach den Methoden, die hier beschrieben werden, bekomm ich ebenfalls einen Fehler. Ansonsten ist und bleibt rownum = 52.
edit:
Nach deiner Methode
select *
from studienleistung s
where ( select count(*)
from studienleistung d
where s.ects < d.ects) = 4;
gibt er nicht mal ein ein Ergebnis aus.
Ausserdem frag ich mich die ganze Zeit, was SQL großartig mit Webentwicklung zu tun hat.
Sicherlich werden die Daten in eine SQL Tabelle gespeichert aber es gibt noch andere Anwendungsbereiche für SQL ausser Webentwicklung.
select *
from studienleistung
where ects is not null
order by ects desc;
Das ist jetzt die Innere. Wenn ich jetzt select *, rownum schreibe, meldet er sofort einen Fehler.
Die äußere kann ich machen, was ich will. Nach den Methoden, die hier beschrieben werden, bekomm ich ebenfalls einen Fehler. Ansonsten ist und bleibt rownum = 52.
...
Ich brauche schon die gesamte Abfrage, nicht nur die innere.
select *, rownum ist falsch, denn entweder du willst implizit alle, oder du gibst die Spalten, die du brauchst explizit an.
Außerdem musst du zuerst ordnen, bevor du ROWNUM benutzt, da ROWNUM sonst auf die ungeordneten Einträge angewendet wird und danach nach ECTS geordnet wird.
Ohne jetzt deine ganzen Bezeichnungen genau zu kennen, sollte deine Abfrage wie folgt aufgebaut sein:
1. Die innerste Abfrage wählt die Tabelle studienleistung und ordnet die Einträge nach ECTS:
SELECT*FROM studienleistung
WHERE ects ISNOTNULLORDERBY ects DESC;
2. Aus dem Ergebnis der innersten Abfrage (also den geordneten Einträgen) wählt die nächstäußere Abfrage die ersten N Einträge mit Hilfe von ROWNUM:
SELECT ects, ROWNUM rn
FROM(ErgebnisDerInnerstenAbfrage)WHERE ROWNUM <= N
Wichtig hier, dass die ROWNUM-Werte, die hier rauskommen in der nächstäußeren Abfrage verfügbar sind, also zwischenspeichern in rn.
3. Nun wählt die äußerste Abfrage aus dem Ergebnis der vorherigen Abfrage (also den ersten N Einträgen der nach ECTS geordneten Einträge aus studienleistung) den untersten Eintrag, also den N-ten Eintrag und zwar mit Hilfe des zuvor in rn gespeicherten ROWNUM-Wertes: