Information ausblenden
Willkommen im Forum für alle Datenbanken! Registriere Dich kostenlos und diskutiere über DBs wie Mysql, MariaDB, Oracle, Sql-Server, Postgres, Access uvm

DB2 - FETCH FIRST XX ROWS only - dynamisch

Dieses Thema im Forum "DB2" wurde erstellt von Andy_K, 19 August 2013.

  1. Andy_K

    Andy_K Neuer Benutzer

    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;
     
  2. ukulele

    ukulele Datenbank-Guru

    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.
     
  3. Andy_K

    Andy_K Neuer Benutzer


    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 :)
     
  4. akretschmer

    akretschmer Datenbank-Guru


    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.
     
  5. Tommi

    Tommi Datenbank-Guru

    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
     
  6. ukulele

    ukulele Datenbank-Guru

    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.
     
  7. Andy_K

    Andy_K Neuer Benutzer

    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');
     
  8. akretschmer

    akretschmer Datenbank-Guru


    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
     
Die Seite wird geladen...

Diese Seite empfehlen

  1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies.
    Information ausblenden