DB2 - FETCH FIRST XX ROWS only - dynamisch

Andy_K

Neuer Benutzer
Beiträge
3
Hallo zusammen,

ich benötige für eine Planung eine bestimmte Absatzmenge diverser Kunden aus einem bestimmten Bestellzeitraum. Die Absatzmenge ist aber pro Kunde begrenzt.
Beispiel mit zwei Kunden (in echt ca. 300 Kunden):
Kunde_1 hat eine Mengenbegrezung von 50, Kunde_2 von 95. Die Begrezungsanzahl ergibt sich aus einer separaten Tabelle.
Wird im SELECT bei dem Kunden_1 eine Menge von 60 zurückgegeben, sollen nur die ersten 50 aufsteigend nach dem Bestelldatum berücksichtigt werden, bei dem Kunden_2 ensprechend nur die ersten 95. Nachfolgender Beispielselect soll das ganze verdeutlichen.
Ich habe versucht die FETCH FIRST Anweisung in einen SELECT einzubinden, was natürlich nicht funktioniert.
Aber wie lässt sich das realisieren?

Hinweis: Datenbank DB2
Programm: DBVisualizer 9.0.8

Mfg. Andy


Tablle aus der die Mengenbegrenzung ausgelesen werden soll.

DECLARE GLOBAL TEMPORARY TABLE
SESSION.TB1
(
TB1_cust_no CHAR (5),
TB1_orde INTEGER
)
ON
COMMIT PRESERVE ROWS;
INSERT INTO SESSION.TB1 VALUES ('Kunde_1', 50);
INSERT INTO SESSION.TB1 VALUES ('Kunde_2, 95);

Select auf die Datenbank

SELECT
cust_no AS Kunde,
artik_no AS Artikel_Nr,
artik_bez AS Artikel_Bezeichnung,
artik_Preis AS Preis,
orde_dat As Bestelldatum
FROM prod.orde
JOIN prod.artikel ON artik_orde_key = orde_key
JOIN prod.cust ON cust_key = orde_cust_key
JOIN SESSION.TB1 ON AZ_cust_no = cust_no
WHERE DATE(orde_dat) BETWEEN '2013-01-01' AND '2013-08-31'
ORDER BY orde_dat
FETCH FIRST (SELECT TB1_orde
FROM SESSION.TB1
WHERE cust_no = TB1_cust_no)
ROWS only
FOR read only with ur;
 
Werbung:

ukulele

Datenbank-Guru
Beiträge
4.702
Also wenn ich das richtig verstehe willst du Werte aus einem SQL Ergebnis in einem SQL Statement weiter verwenden. Das geht meines Wissens nach immer nur, wenn du dein Statement mit dynamischem SQL zusammen (z.B. als VARCHAR) baust und mit EXEC() ausführt.
 

Andy_K

Neuer Benutzer
Beiträge
3
Also wenn ich das richtig verstehe willst du Werte aus einem SQL Ergebnis in einem SQL Statement weiter verwenden. Das geht meines Wissens nach immer nur, wenn du dein Statement mit dynamischem SQL zusammen (z.B. als VARCHAR) baust und mit EXEC() ausführt.


Hallo ukulele,

vielleicht verstehe ich nicht ganz was Du meinst, aber Werte aus einem SQL Ergebnis in einem weiteren Statement verwenden geht schon, das mache ich in vielen anderen Abfragen auch
(SUB SELECT). In meinen Beispiel stammen die Werte aus der temporären Tabelle auf die ich JOINE, das macht die Sache ja eigentlich einfacher.
Was sicherlich nicht geht ist, den Rückgabewert (Anzahl) in die FETCH FIRST-Anweisung einzubinden (mein SELECT-Beispiel).
Die reguläre FETCH Anweisung sieht ja so aus: FETCH FIRST 60 ROWS only , und gibt die ersten 60 Zeilen des SELECT zurück.
Mein Ansatz war auch nur ein erbärmlicher Versuch, da mir nichts anderes eingefallen ist :)
 

akretschmer

Datenbank-Guru
Beiträge
9.736
Hallo ukulele,

vielleicht verstehe ich nicht ganz was Du meinst, aber Werte aus einem SQL Ergebnis in einem weiteren Statement verwenden geht schon, das mache ich in vielen anderen Abfragen auch
(SUB SELECT). In meinen Beispiel stammen die Werte aus der temporären Tabelle auf die ich JOINE, das macht die Sache ja eigentlich einfacher.
Was sicherlich nicht geht ist, den Rückgabewert (Anzahl) in die FETCH FIRST-Anweisung einzubinden (mein SELECT-Beispiel).
Die reguläre FETCH Anweisung sieht ja so aus: FETCH FIRST 60 ROWS only , und gibt die ersten 60 Zeilen des SELECT zurück.
Mein Ansatz war auch nur ein erbärmlicher Versuch, da mir nichts anderes eingefallen ist :)


IIRC sollte das gehen, um es mal ganz trivial zu testen:

Code:
test=*# create table foo as select * from generate_series(1,10) s;
SELECT 10
test=*# select * from foo limit (select 5);
 s
---
 1
 2
 3
 4
 5
(5 rows)

Aber ob das auch in DB2 geht weiß ich mangels Erfahrung und DB2-Zugang nicht. Ukulele meint, Du mußt in einer Funktion Dir einen String zusammenkleben, welche das endgültige SQL enthält, und diesen String dann in der Funktion via EXECUTE ausführen.
 

Tommi

Datenbank-Guru
Beiträge
290
Hi,

ich kann zwar jetzt kein DB2, aber DB2 kann doch bestimmt Fensterfunktionen.
Ich hätte das jetzt so gelöst, dass ich die Temporäre Tabelle als führend abfrage und die Kundenbestellungen in einem Sub-Select hinzu joine.
Im Subselect sollte dann für jeden Kunden eine fortlaufende Nummer (LFD_Nr) für eine Bestellung existieren (was dann über eine Fensterfunktion, beim SQL Server z.B. ROW_NUMBER() ) zu lösen wäre.

Die Verknüpfungsbedingungen des JOINS wären dann LFD_NR <= TB1_cust_no .

Wie die Syntax mit der Fensterfunktion in DB2 aussieht und ob DB2 sowas kann, weiß ich allerdings nicht.

Viele Grüße,
Tommi
 

ukulele

Datenbank-Guru
Beiträge
4.702
Ich benutze fast nur MSSQL und du hast in sofern recht das TOP(SELECT 1) da auch geht. In jedem anderen Fall den ich bisher hatte war ich aber bisher nicht in der Lage z.B. einen VARCHAR Wert auszulesen und in der WHERE Bedingung als Spaltenname zu nutzen. Die Lösung ist dann immer ein Statement als String zusammen zu setzen und auszuführen.
 

Andy_K

Neuer Benutzer
Beiträge
3
Danke zunächst für die Rückmeldungen.
Ich bin noch auf der Suche nach der passenden Lösung, komme der Sache aber schon etwas näher.

Die Auswertung die ich machen möchte setzt auf eine bestehende Auswertung auf.
Dieses Statement beninhaltet aktuell "Zeilen = 2312 / Zeichenanzahl = 214222 / Anzahl Wörter = 25647".
Das ganze ist recht kompliziert und deshalb auch nicht in ein paar Worten zu erklären.

Ich habe mal den Ansatz von Tommi weiterverfolgt. Mit einem Kunden funktioniert das schon ganz gut.
Da die Nummerierung für jeden Kunden/Artikel wieder mit 1 beginnen muss, müsste ich das Statement für jeden Kunden separat laufen lassen und die Ergebnisse
in einer weiteren Tabelle sammeln.
Da das bestehende Statement ohnehin schon sehr komplex ist, möchte ich mir diesen Aufwand eingentlich ersparen.

Mein Ansatz sieht aktuell wie folgt aus.

Sollte doch noch jemand eine Idee haben, einfach posten.

Danke

Andy

Code:
DECLARE GLOBAL TEMPORARY TABLE
SESSION.T1
        ( T1_ZAEHLER INT GENERATED ALWAYS AS IDENTITY
        ( START WITH 1,
          INCREMENT BY 1 ),
          T1_DUMMY INTEGER NOT NULL)
ON COMMIT PRESERVE ROWS;
 
INSERT INTO
    SESSION.T1
        (
        T1_DUMMY
        )
SELECT artik_KEY
FROM prod.artikel
FETCH FIRST 1000 ROWS ONLY;
 
DECLARE GLOBAL TEMPORARY TABLE
SESSION.T2
        ( T2_SA SMALLINT NOT NULL WITH DEFAULT 1)
ON COMMIT PRESERVE ROWS;
 
INSERT INTO
SESSION.T2
        (
        T2_SA
        )
SELECT T1_ZAEHLER
FROM SESSION.T1
WHERE T1_ZAEHLER BETWEEN 1 AND
        (
          SELECT COUNT(*)
          FROM prod.ORDE
          WHERE ORDE_CUST_KEY IN
        (
          SELECT CUST_KEY
          FROM prod.CUST
          WHERE CUST_NO = 'Kunde_1')
          AND DATE(ORDE_DATE) BETWEEN '01.01.2012' AND '31.01.2012');
 
Werbung:

akretschmer

Datenbank-Guru
Beiträge
9.736
Da die Nummerierung für jeden Kunden/Artikel wieder mit 1 beginnen muss, müsste ich das Statement für jeden Kunden separat laufen lassen und die Ergebnisse
in einer weiteren Tabelle sammeln.


Ich biete da mal folgendes:

Code:
test=*# select * from foo;
  kunde  | val
---------+-----
 kunde1  |   1
 kunde1  |   2
 kunde1  |   4
 kunde1  |   7
 kunde1  |   3
 kunde2  |  20
 kunde20 |  21
 kunde2  |  22
 kunde2  |  23
 kunde2  |  24
(10 rows)

test=*# select * from foo2;
 kunde  | anzahl
--------+--------
 kunde1 |      3
 kunde2 |      2
(2 rows)

test=*# select * from (select foo.*, row_number() over (partition by kunde), foo2.anzahl from foo left join foo2 using (kunde)) bla where row_number <= anzahl;
 kunde  | val | row_number | anzahl
--------+-----+------------+--------
 kunde1 |   2 |          1 |      3
 kunde1 |   1 |          2 |      3
 kunde1 |   4 |          3 |      3
 kunde2 |  24 |          1 |      2
 kunde2 |  20 |          2 |      2
(5 rows)

Es fehlt natürlich noch ein Sortierkriterium, wie die Kunden da zu zählen sind. Das gehört noch in die over() - Definition.

Andreas
 
Oben