![]() |
Datenbank: FB 1.5 • Zugriff über: FIBplus
Problem mit stored Procedure und group by
Hi,
ich bin hier mit einer Statistik beschäftigt. Es sollen die Mengen eines Kunden über ein paar Monate addiert werden. Diese sollen zusammen mit anderen Informationen ausgegeben werden. Ich habe das jetzt mal so reduziert :
SQL-Code:
Das Problem ist das A.NR (für Artikelnummer). Es heißt, das wäre weder in einer aggregate function noch in einer group by clause vorhanden. SUM usw. brauche ich ja auch dafür nicht. Aber wo muß das GROUP BY genau hin ? Auf Dataset Ebene geht alles, aber in Delphi ist das komplette Statement fast eine DIN A 4 Seite. Ich will zumindest die Sachen, die nur die DB betreffen in die SP verlagern.
SELECT SUM(K.MENGE), A.NR
FROM ART A RIGHT JOIN KSTAT K ON (A.ID = K.ID_ART) WHERE (MONAT >= :VONMONAT) AND (MONAT <= :BISMONAT) AND (JAHR = :JAHR) INTO MONATSMENGE; |
Re: Problem mit stored Procedure und group by
Hallo Hansa,
laut SQL Reference gehört "GROUP BY A.Nr" hinter deine INTO-Klausel. Grüße vom marabu |
Re: Problem mit stored Procedure und group by
Hallo Hanso,
probiere es mal so:
SQL-Code:
evt hilft es auch wenn Du anstatt SUM(K.MENGE) lieber SUM(K.MENGE) AS Feldname schreibst.
SELECT SUM(K.MENGE), A.NR
FROM ART A RIGHT JOIN KSTAT K ON (A.ID = K.ID_ART) WHERE (MONAT >= :VONMONAT) AND (MONAT <= :BISMONAT) AND (JAHR = :JAHR) GROUP BY A.NR INTO MONATSMENGE; Feldname sollte dabei dem Ziel Feld in der Tabelle MONATSMENGE entsprechen. |
Re: Problem mit stored Procedure und group by
hinter dem Into bringt : "Parsing error"
davor bringt : "count of column list and variable list do not match." AS bringt auch keine Änderung. @Jens : Monatsmenge ist keine Tabelle, sondern ein Rückgabewert ! Odder liegt es daran ? Ich befürchte langsam, daß das ganz anders gemacht werden muß. 8) |
Re: Problem mit stored Procedure und group by
Muss das nicht so aussehen?
SQL-Code:
Bei ORACLE muss das INTO zumindest immer nach dem Select kommen.
SELECT SUM(K.MENGE), A.NR INTO MONATSMENGE
FROM ART A RIGHT JOIN KSTAT K ON (A.ID = K.ID_ART) WHERE (MONAT >= :VONMONAT) AND (MONAT <= :BISMONAT) AND (JAHR = :JAHR) GROUP BY A.NR; Gruß Daniel |
Re: Problem mit stored Procedure und group by
Zitat:
Bei IB/FB kommt das Into hinter WHERE/ORDER BY clause aber vor die Gruppierung (solange ich es richtig in Erinnerung habe). |
Re: Problem mit stored Procedure und group by
Er hat es nicht gelöst. Das war das erste, was ich vorher schon gemacht habe. Allerdings muß es laut SQL-Referenz davor, wie ens schreibt ! Ergebnis : siehe oben. 8)
|
Re: Problem mit stored Procedure und group by
Hallo Hansa,
kann MONATSMENGE 2 Werte aufnehmen??? |
Re: Problem mit stored Procedure und group by
Hansa, so wird das nie was.
Zitat:
Grüße vom marabu |
Re: Problem mit stored Procedure und group by
ALSO:
wenn du in einer SQL-Abfrage eine Aggregat-Funktion benützt (SUM, MIN, MAX, COUNT, AVG), dann werden mehrere Zeilen zusammengefasst.
SQL-Code:
Hier sind die Felder MONAT & JAHR nicht eindeutig, da kein Tabellenname angegeben. Daher:
SELECT SUM(K.MENGE)
FROM ART A RIGHT JOIN KSTAT K ON (A.ID = K.ID_ART) WHERE (MONAT >= :VONMONAT) AND (MONAT <= :BISMONAT) AND (JAHR = :JAHR)
SQL-Code:
Wenn in der Feldliste dann neben der Aggregat-Funktion ein (oder mehrere) "normales" Feld(er) auftaucht, dann muss diese Feld hinter GROUP BY stehen!!
SELECT SUM(K.MENGE)
FROM ART A RIGHT JOIN KSTAT K ON (A.ID = K.ID_ART) WHERE (K.MONAT >= :VONMONAT) AND (K.MONAT <= :BISMONAT) AND (K.JAHR = :JAHR)
SQL-Code:
Wenn du die Klausel SELECT ..... INTO new_table verwendest, dann gibt die Abfrage kein Ergebnis zurück, sondern die Ergebnismenge wird in eine neue physikalische Tabelle kopiert.
SELECT SUM(K.MENGE), A.NR
FROM ART A RIGHT JOIN KSTAT K ON (A.ID = K.ID_ART) WHERE (K.MONAT >= :VONMONAT) AND (K.MONAT <= :BISMONAT) AND (K.JAHR = :JAHR) GROUP BY A.NR Du kannst auch die Daten mit einer Unterabfrage aufbereiten. Zuerst die innere SELECT - Abfrage:
SQL-Code:
Diese Unterabfrage lässt sich nun mit der Tabelle ART verjoinen:
(SELECT ID_ART, SUM(MENGE) as SUMMENGE FROM KSTAT
WHERE (MONAT >= :VONMONAT) AND (MONAT <= :BISMONAT) AND (JAHR = :JAHR) GROUP BY ID_ART)
SQL-Code:
Die Tabelle DevTbl ist der Alias für die Unterabfrage.
SELECT A.*, DevTbl.SUMMENGE
FROM ART A RIGHT JOIN (SELECT ID_ART, SUM(MENGE) as SUMMENGE FROM KSTAT WHERE (MONAT >= :VONMONAT) AND (MONAT <= :BISMONAT) AND (JAHR = :JAHR) GROUP BY ID_ART) DevTbl Vorteil: du hast Zugriff auf alle Felder der Tabelle ART. Es gibt also zwei Möglichkeiten: * zuerst einen JOIN bilden and dann mit GROUP BY verdichten * zuerst mit GROUP BY verdichten (die Unterabfrage) und dann den JOIN bilden Ich hoffe, das bringt etwas Licht ins Dunkel :angel2: |
Re: Problem mit stored Procedure und group by
Zitat:
marabu |
Re: Problem mit stored Procedure und group by
Zitat:
Wenn man bei Firebird mit SELECT .... INTO Variablen befüllt, dann sollte natürlich die Anzahl der Spalten bei SELECT mit der Anzahl der angegebenen Variablen übereinstimmen. Ich könnte mir auch vorstellen, dass bei SELECT INTO nur ein Datensatz von SELECT geliefert werden darf, da eine Variable nur einen Wert annehmen kann. |
Re: Problem mit stored Procedure und group by
Hallo Andreas,
Zitat:
Freundliche Grüße vom marabu |
Re: Problem mit stored Procedure und group by
Wie shmia das gesagt hat, so wäre es genau richtig. Mir gelingt es leider immer noch nicht, sofern ein SUM usw. im Select drin steht, noch ein anderes Feld dazuzupacken. Wer will kann sich die DB mal ansehen oder das Script ansehen. Also in IBExpert DB registrieren oder das Script ausführen.
Hier ist anscheinend jede Erweiterung verboten. Nicht mal ohne Erweiterung ? SQL, FDB alles verboten ?? :shock: .1 und .2 geht auch nicht. Na das ist aber doch jetzt schwer übertrieben ! :evil:
SQL-Code:
Da ist sogar der Join jetzt raus.
/******************************************************************************/
/**** Generated by IBExpert 2005.06.07 29.08.2005 20:50:59 ****/ /******************************************************************************/ SET SQL DIALECT 3; SET NAMES ISO8859_1; CREATE DATABASE 'C:\db\TESTDB.FDB' USER 'SYSDBA' PASSWORD 'masterkey' PAGE_SIZE 4096 DEFAULT CHARACTER SET ISO8859_1; /******************************************************************************/ /**** Generators ****/ /******************************************************************************/ CREATE GENERATOR GEN_ART_ID; SET GENERATOR GEN_ART_ID TO 2; CREATE GENERATOR GEN_KSTAT_ID; SET GENERATOR GEN_KSTAT_ID TO 6; SET TERM ^ ; /******************************************************************************/ /**** Stored Procedures ****/ /******************************************************************************/ CREATE PROCEDURE TESTSP ( VONMONAT INTEGER, BISMONAT INTEGER, ID_ART INTEGER) RETURNS ( MONATSMENGE INTEGER) AS BEGIN EXIT; END^ SET TERM ; ^ /******************************************************************************/ /**** Tables ****/ /******************************************************************************/ CREATE TABLE ART ( ID INTEGER NOT NULL, NR INTEGER, BEZ CHAR(30) ); CREATE TABLE KSTAT ( ID INTEGER NOT NULL, ID_ART INTEGER, MONAT INTEGER, MENGE INTEGER ); /******************************************************************************/ /**** Primary Keys ****/ /******************************************************************************/ ALTER TABLE ART ADD CONSTRAINT PK_ART PRIMARY KEY (ID); ALTER TABLE KSTAT ADD CONSTRAINT PK_KSTAT PRIMARY KEY (ID); /******************************************************************************/ /**** Foreign Keys ****/ /******************************************************************************/ ALTER TABLE KSTAT ADD CONSTRAINT FK_KSTAT_1 FOREIGN KEY (ID_ART) REFERENCES ART (ID); /******************************************************************************/ /**** Triggers ****/ /******************************************************************************/ SET TERM ^ ; /******************************************************************************/ /**** Triggers for tables ****/ /******************************************************************************/ /* Trigger: ART_BI0 */ CREATE TRIGGER ART_BI0 FOR ART ACTIVE BEFORE INSERT POSITION 0 AS begin if (new.ID is null) then new.ID = gen_id(GEN_ART_ID, 1); end ^ /* Trigger: KSTAT_BI0 */ CREATE TRIGGER KSTAT_BI0 FOR KSTAT ACTIVE BEFORE INSERT POSITION 0 AS begin if (new.ID is null) then new.ID = gen_id(GEN_KSTAT_ID, 1); end ^ SET TERM ; ^ /******************************************************************************/ /**** Stored Procedures ****/ /******************************************************************************/ SET TERM ^ ; ALTER PROCEDURE TESTSP ( VONMONAT INTEGER, BISMONAT INTEGER, ID_ART INTEGER) RETURNS ( MONATSMENGE INTEGER) AS DECLARE VARIABLE MENGE INTEGER; begin SELECT SUM(MENGE) /*,MENGE */ FROM KSTAT WHERE (ID_ART = :ID_ART) AND (MONAT >= :VONMONAT) AND (MONAT <= :BISMONAT) INTO :MONATSMENGE /*GROUP BY MENGE*/; suspend; end ^ SET TERM ; ^ |
Re: Problem mit stored Procedure und group by
Hallo hansa,
ich bin mir zwar nicht ganz sicher was du als Ergebnis sehen möchtes aber.. Zitat:
Ich denke nach Monat wäre das Richtige. Das Problem ist aber, dass "GROUP BY" mehrere Gruppen (Datensätze) als Ergebnis erzeugen kann. Dein Statement aber nur ein singleton-Ergebnis zuläßt. Für meherer Ergebissätze mußt du das SELECT in ein FOR ..DO packen.
SQL-Code:
Verwenden könnte man es z.B. so:
CREATE PROCEDURE TESTSP ( VONMONAT INTEGER, BISMONAT INTEGER, ID_ART INTEGER)
RETURNS ( MONAT INTEGER, MONATSMENGE INTEGER) AS DECLARE VARIABLE MENGE INTEGER; begin FOR SELECT MONAT, SUM(MENGE) FROM KSTAT WHERE (ID_ART = :ID_ART) AND (MONAT >= :VONMONAT) AND (MONAT <= :BISMONAT) group by Monat INTO :Monat, :MONATSMENGE DO suspend; end
SQL-Code:
alex
select A.*, T.MONAT, T.MONATSMENGE
from art A LEFT join TESTSP(:VonMonat,:BisMonat, A.ID) T on (1=1) where A.NR=2 |
Re: Problem mit stored Procedure und group by
Liste der Anhänge anzeigen (Anzahl: 1)
Hi Alex,
Shmias Code wäre für mich schon am sinnvollsten von der Logik her zumindest. Auf das FOR SELECT bin ich auch gestoßen, da war allerdings ein ähnlicher Effekt zu bemerken. SPs kommen leider überall viel zu kurz. Für das gepostete Beispiel-Script wäre ein besserer Vergleich die ID_ART gewesen (statt der Menge), denn da ist letztenendes ein Join über die Artikel vorgesehen (siehe Thread Anfang). Im Anhang ist das Ergebnis. Nochmals zur Erklärung : geliefert werden 3 Zeilen. Habe von Monat 1-3 eingegeben. Ich bräuchte jetzt eine Zeile mit Wert MONATSMENGE : 9, also 2+3+4 Also nicht die Menge pro Monat, sondern die Gesamtmenge. |
Re: Problem mit stored Procedure und group by
wenn du je Artikel aus der Tabelle ART nur eine Gesamtmenge
benötigst, hattest du eigentlich bereits die Lösung mit deiner SP. Für die Summe über alles (das SELECT darf dann nur Aggregat-Funktionen beinhalten) wird keine GROUP BY benötigt. Ohne GROUP BY wird auch immer nur ein Datensatz in der Ergebnismenge zurückgegeben. Damit wird FOR..DO.. überflüssig. Es fehlte nur der richtige Aufruf der SP. Hier nochmal die SP für die Gesamtmengen (ich habe MONATSMENGE in GESMENGE umbenannt)
SQL-Code:
und der Aufruf
CREATE PROCEDURE TESTSP_GES (
VONMONAT INTEGER, BISMONAT INTEGER, ID_ART INTEGER) RETURNS ( GESMENGE INTEGER) AS begin SELECT SUM(MENGE) FROM KSTAT WHERE (ID_ART = :ID_ART) AND (MONAT >= :VONMONAT) AND (MONAT <= :BISMONAT) INTO :GESMENGE; IF (GESMENGE is NULL) THEN GESMENGE = 0; suspend; end
SQL-Code:
das Ganze könne man aber auch damit erreichen
select A.NR, A.BEZ, T.GESMENGE
from art A left join TESTSP_GES(:VonMonat,:BisMonat, A.ID) T on (1=1)
SQL-Code:
wichtig ist nur, daß in GROUP BY alle Nicht-Aggregat-Felder enthalten sind.
SELECT A.NR, A.BEZ, COALESCE(SUM(K.MENGE),0)
from ART A left join KSTAT K on (K.ID_ART=A.ID) GROUP BY 1, 2 alex |
Re: Problem mit stored Procedure und group by
Thx, Alex
So gehts ! :thumb: War allerdings echt schwere Geburt. 8) Baue jetzt mal die Original-DB um. |
Re: Problem mit stored Procedure und group by
Original SP geht auch, bis auf das hier :
SQL-Code:
Mit "0" geht das zwar, aber ich habe gesehen, daß da doch ziemlich viele NULL Werte drin sind. Wie grenze ich denn die jetzt noch aus ? :shock:
HAVING (SUM (UMSATZ) <> NULL)
Edit : es muß ja zumindest
SQL-Code:
heißen oder so ähnlich. Wie aber genau ? Wo muß das NOT jetzt hin ? Das Problem ist, daß ich 5 Felder habe und eine Anzeuge nicht erscheinen soll, sofern alle 5 NULL oder 0 sind.
HAVING (NOT SUM (UMSATZ) IS NULL)
SQL-Code:
Oder ist da ein logischer Denkfehler drin ? :gruebel:
HAVING NOT ((SUM (MENGE) IS NULL) OR (SUM (UMSATZ) IS NULL)) OR ...
|
Re: Problem mit stored Procedure und group by
Weiß keiner mehr was außer Alex ? Ich hänge nämlich wieder fest. :mrgreen:
So was wie hier geht mit IBExpert einwandfrei:
SQL-Code:
Aber wie greife ich denn jetzt mit Delphi darauf zu ?
select A.NR, A.BEZ, T.GESMENGE
from art A left join TESTSP_GES(:VonMonat,:BisMonat, A.ID) T on (1=1) |
Re: Problem mit stored Procedure und group by
Hi Hansa,
Zitat:
Sollte es aber um die GESMENGE gehen, so kann diese NULL nicht enthalten, wenn man in der SP vor SUSPEND
SQL-Code:
einfügt. Das kann man auch immer einfach auf ..WHERE GESMENGE>0 prüfen.
IF (GESMENGE is NULL) THEN GESMENGE = 0;
Zitat:
Delphi-Quellcode:
alex
procedure TForm1.Button1Click(Sender: TObject);
{ FIBDataSet1.SQLs.SelectSQL: select A.NR, A.BEZ, T.GESMENGE from art A left join TESTSP_GES(:VonMonat,:BisMonat, A.ID) T on (1=1) @@Filter@ } begin FIBDataSet1.Close; FIBDataSet1.ParamByName('VonMonat').AsInteger := StrToIntDef(EVon.Text, 0); FIBDataSet1.ParamByName('BisMonat').AsInteger := StrToIntDef(EBis.Text, 999999); if CBOhne0Mengen.Checked then FIBDataSet1.ParamByName('Filter').AsString := 'where GESMENGE>0' else FIBDataSet1.ParamByName('Filter').AsString := ''; FIBDataSet1.open; end; |
Re: Problem mit stored Procedure und group by
Vor dem Suspend das NULL auswerten ? Oh je, da hätte ich auch selber draufkommen können. 8) Nun gut, das geht natürlich jetzt auch. Aber wo zum Teufel kommen die ganzen Klammeraffen da her ? :shock: Bzw., was machen die ? Die Checkboxen kommen natürlich auch noch ins Spiel und nicht nur eine ! Aber es wird sowieso immer komplizierter. Konkret geht es um 5 SUM-Felder, eines ist das SUM (UMSATZ) und da wirds jetzt richtig kompliziert. Wie baue ich jetzt da noch eventuell eine Mwst.-SP ein, die den Brutto Umsatz errechnet ? Nur so als Vorschau. 8)
|
Re: Problem mit stored Procedure und group by
Zitat:
Damit kann man in einem FIBDataSet z.B. bequem WHERE, ORDER oder sonstige Klauseln setzen, ändern und auch wieder löschen. Zitat:
alex |
Re: Problem mit stored Procedure und group by
Ja, ich habe tatsächlich einige Wechselstaben verbuchtelt, oder wie das heißt. :mrgreen: Zu den Klammeraffen : da ist eine sagenhafte fast nichtssagende Seite über Macro zu finden. Wieso sind da 2 @ und hinten noch einer ? Laut dieser Seite müßte es doch reichen nur @Filter zu schreiben ? Und dann das % usw. wozu ist das gut ? Das ganze paßt irgendwie nicht so richtig in die Firebird-Syntax. Werde wohl mal den SQL-Monitor anschmeißen.
|
Re: Problem mit stored Procedure und group by
Die Macros sind eine reine FIBPlus-Funktionalität, hat also nichts mit Firebird zu tun.
Ich gebe zu es steht nicht viel in der FibPlus53.pdf und außerdem haben ich es mir auch erklären lassen. Das Macro fängt mit (@@) an und endet mit (@). Hinter dem (%) kann man den Default-Wert eintragen. Dieser kommt zu Geltung wenn das Marco nicht über ParamByName gesetzt wird. (#) setzt man wenn der Ausdruck eigentlich in ('..') stehen muß zB. bei Strings oder Datumswerten. where DatumVon>=@@Datum%#01.01.1980@ FibPlus macht daraus: a) wenn Marco nicht gesetzt wird: where DatumVon>='01.01.1980' b) wenn Marco gesetzt wird mit FibDataSet.ParamByName('DatumVon').AsDateTime = StrToDate('25.05.2005') where DatumVon>='25.05.2005' Ich setze Macros aber bisher nur in der einfachen Form ein. alex |
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:03 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz