Re: Abfrage bei Sybase SQL Anywhere 9



* Andreas Bihler:

Angenommen wir haben folgende Einträge:
0123, Test1, 2, 31.10.2006
4567, Test2, 1, 10.01.2008
0123, Test1, 2, 11.02.2008

Nun gebe ich als Zeitgrenze den 01.02.2008 an.
Als Ergebnis müsste dann folgendes kommen:
4567, Test2, 1, 10.01.2008

Ist das der Eintrag für den letzten Verkauf? Das ist mit Standard-SQL
eher schwierig umzusetzen, fürchte ich, gerade wenn die Tabelle keinen
Primärschlüssel hat. Wenn Sybase so etwas wie DISTINCT ON kann, dann
geht es aber. Die infragekommenden Artikelnummern bekommst Du jedenfalls
mit:

SELECT ArtikelNr FROM Tabelle
EXCEPT SELECT ArtikelNr FROM Tabelle WHERE Datum > '2008-02-01'

Jetzt mußt Du noch den Kompletten Datensatz dazu finden. Mit DISTINCT ON
ist es relativ einfach:

SELECT DISTINCT ON (ArtikelNr) *
WHERE ArtikelNr IN (SELECT von oben)
ORDER BY Datum DESC

Das hat den Vorteil, daß man die Tabellenspalten nicht explizit
aufführen muß, so daß diese Abfrage recht wartungsarm ist.


Ohne DISTINCT ON ist es eher schwierig. Wir müssen erst einmal einen
Primärschlüssel in der passenden Sortierung erzeugen:

SELECT COUNT(*) AS pk, t1.* FROM Tabelle t1, Tabelle t2
WHERE (t1.Datum, t1.ArtikelNr, t1.ArtikelName, t1.Anzahl)
>= (t2.Datum, t2.ArtikelNr, t2.ArtikelName, t2.Anzahl)

Es kann sein, daß Transact-SQL keine Tupel-Vergleiche unterstützt, dann
muß man das eben manuell hinschreiben:

t1.Datum > t2.Datum
OR (t1.Datum = t2.Datum AND t1.ArtikelNr > t2.ArtikelNr)
OR (t1.Datum = t2.Datum AND t1.ArtikelNr = t2.ArtikelNr AND ...)
...

Am besten erzeugt man soetwas mit einem kleinen Skript (wie auch den
folgenden Sermon).

Mit ROWNUM, generate_series o.ä. kann man den Self-Join mit seinem
quadratischen Laufzeitverhalten loswerden, zum Beispiel:

SELECT ROWNUM AS pk, * FROM Tabelle ORDER BY Datum

Oder, was neueres Sybase eher unterstützt:

SELECT RANK() OVER (ORDER BY Datum) AS pk, * FROM Tabelle

(Der Self-Join-Trick hat auch Probleme mit Duplikaten, was aber im
folgenden keine Rolle spielen sollte.)

Wenn man das gebastelt hat, kann man zu jedem Artikel einen beliebigen
letzten Kaufvorgang zu ermitteln:

WITH TabellePK (pk, ArtikelNr, ArtikelName, Anzahl, Datum) AS (
SELECT RANK() OVER (ORDER BY Datum) AS pk, * FROM Tabelle
)
SELECT ArtikelNr, MAX(pk) FROM TabellePK GROUP BY ArtikelNr

(Keine Ahnung ob die Syntax en detail korrekt ist, ich kenne WITH nur
aus der Dokumentation, da PostgreSQL das nicht unterstützt. Das WITH ist
aber hier und im folgenden nicht essentiell, man kann das über eine
temporäre Tabelle emulieren oder über manuelles Expandieren des
SELECTs.)

Wenn wir das mit der anfänglichen Abfrage verheiraten, kommt folgendes
heraus:

WITH TabellePK (pk, ArtikelNr, ArtikelName, Anzahl, Datum) AS (
SELECT RANK() OVER (ORDER BY Datum) AS pk, * FROM Tabelle
)
SELECT t1.ArtikelNr, t1.ArtikelName, t1.Anzahl, t1.Datum
FROM TabellePK t1,
(SELECT ArtikelNr, MAX(pk) FROM TabellePK GROUP BY ArtikelNr) t2
WHERE t1.pk = t2.pk
AND t2.ArtikelNr IN (SELECT ArtikelNr FROM Tabelle
EXCEPT SELECT ArtikelNr
FROM Tabelle WHERE Datum > '2008-02-01')

Oder auch:

WITH TabellePK (pk, ArtikelNr, ArtikelName, Anzahl, Datum) AS (
SELECT RANK() OVER (ORDER BY Datum) AS pk, * FROM
(SELECT * FROM Tabelle
EXCEPT SELECT * FROM Tabelle WHERE Datum > '2008-02-01') t
)
SELECT t1.ArtikelNr, t1.ArtikelName, t1.Anzahl, t1.Datum
FROM TabellePK t1,
(SELECT ArtikelNr, MAX(pk) FROM TabellePK GROUP BY ArtikelNr) t2
WHERE t1.pk = t2.pk

Das sollte, was die Effizienz der Ausführung angeht, mit dem
DISTINCT-ON-Ansatz vergleichbar sein, da der teure Teil das EXCEPT ist.
Wenn Dein Sybase noch kein RANK() hast, wird es aber ziemlich übel.

(Alles natürlich mangels Sybase ungetest. 8-)
.



Relevant Pages

  • Re: Schnelles Auffinden doppelter Daten
    ... als einmal in der Tabelle vorkommt. ... CREATE TABLE #arbeitstabelle ... INSERT INTO #arbeitstabelle SELECT DISTINCT key, val FROM tabelle ...
    (microsoft.public.de.german.entwickler.dotnet.datenbank)
  • Re: Gewisse Zeilenwerte addieren
    ... > FROM Tabelle ... > UNION ALL SELECT Tabelle.IDLoser as ID,> Tabelle.ScoreLoser as Score, ... > auf Basis dieser eine Abfrage zur Berechnung der> Summen: ...
    (microsoft.public.de.access)
  • Re: FAQ 3.16 mit spezieller einschraenkung der Datumsrange in der A-Tabelle
    ... den in Tabelle B gegebenen Datumsrange eingegrenzt sein, ... SELECT A.* ... FROM A LEFT JOIN B ON A.ID=B.ID ... AND A.DATUM BETWEEN MINAND MAX; ...
    (microsoft.public.de.access)
  • Re: Unterabfrage in SP
    ... FROM dbo.tbl_material inner join ... Wie kann ich einen Multiplikator aus einer anderen Tabelle angeben? ... Select ZuÄnderndesFeld, NeuerWertWieAuchImmerBerechnet ...
    (microsoft.public.de.access)
  • Re: Zufällige 4 zeilen aus tabelle ausgeben?
    ... SELECT TOP 4 RNDAS ArtikelNr, pos FROM Produkte ORDER BY pos ... Du sollst nach der erzeugten Zufallszahl sortieren und nicht daraus eine ArtikelNr erzeugen. ... dann ist pos ein Feld in der Tabelle Produkte. ...
    (microsoft.public.de.german.entwickler.dotnet.asp)