Bestands-Daten für die Verwendung in einem Unique-Index anpassen.

Themen rund um den praktischen Einsatz von Firebird. Fragen zu SQL, Performance, Datenbankstrukturen, etc.

Moderator: thorben.braun

bfuerchau
Beiträge: 349
Registriert: Mo 7. Mai 2018, 18:09

Hm, mit welcher Sprache machst du das Ganze?

Und ich glaube, du hast da ein kleines Wort am Ende vergessen:

Code: Alles auswählen

Set   :LAST_ZWECK = :ZWECK;
Hamburgo
Beiträge: 87
Registriert: Di 28. Mai 2019, 17:28

Hallo bfuerchau,

ich mache das Ganze mit PHP.

Gern habe ich Deine Anregung aufgenommen und das Wort "SET" an der
beschriebenen Stelle eingefügt.

Hat leider nichts gebracht.

Die Syntax-Prüfung von FireBird meckert weiterhin den Doppelpunkt
in Zeile 18 / Position 13 in der INTO-Zeile an.

In Zeile 28, wo ich das SET eingebaut habe, kommt der Check scheinbar
gar nicht erst.

Es ist zum Haare raufen.
vr2
Beiträge: 141
Registriert: Fr 13. Apr 2018, 00:13

Hallo Hamburgo,

habe mal eine Tabelle buchungen angelegt, um den Code testen zu können. Folgenden execute block kann ich in Flamerobin laufen lassen, Firebird 4. Falls Du auf Firebird 25 testest, da dürfen die Doppelpunkt-Variablen nicht als Ziel der Zuweisung so geschrieben werden, also :last_zweck = :zweck geht da nicht, sondern last_zweck = :zweck. Dann noch die set term-Geschichte, die aber bei Ausführung in php keine Rolle spielt. Welches Firebird-php-API benutzt Du denn? Doch bestimmt das von Martin ;)

Und schließlich hab ich das ganze mal lesbarer formatiert, ich fand es zunächst schwer zu erkennen, was Du da vorhast. Du willst doch die Statements so lesen können wie sonst auch, wie außerhalb von execute blocks oder stored procedures. Bei declare variable reicht declare. Bei order by xy asc kannst Du asc weglassen, das ist der default (aufsteigend sortieren). Leerzeilen in EBs oder SPs würde ich nur zum visuellen Strukturieren von inhaltlichen/logischen Abschnitten verwenden.

Dann evtl noch ein logischer Fehler, die Zuweisung last_zweck = zweck müsste mMn innerhalb der for-select-Schleife stehen, sonst aktualisierst Du den Wert ja nur einmal, am Schluss. Hier eine lauffähige Version, ich hab nur die domains durch native Typen int und varchar ersetzt, weil ich die domains nicht habe:

Code: Alles auswählen

execute block
as
  declare id int;
  declare zweck varchar(100);
  declare last_zweck varchar(100) = '';
begin
  for select rid_buchungen, verwendungszweck
  from buchungen
  where rid_buchungen > 0
  order by verwendungszweck
  into :id, :zweck do
  begin
    if (:zweck = :last_zweck ) then
    begin
      update buchungen
      set verwendungszweck = :zweck || ' R' || :id
      where rid_buchungen = :id;
    end
    last_zweck = :zweck;
  end
end
Einen Unterschied beim Aufruf könnte auch machen, dass der execute block nichts zurückgibt. Muss er ja nicht unbedingt, aber evtl findet das die aufrufende php-Funktion nicht lustig.

Viele Grüße, Volker
Hamburgo
Beiträge: 87
Registriert: Di 28. Mai 2019, 17:28

Hallo zusammen,

nun bin ich ein bisschen weiter gekommen.

Das Syntax-Problem mit dem Doppelpunkt ist geklärt.

Der Grund, warum ich immer eine Fehler-Meldung wegen des Doppelpunkts
in der INTO-Zeile erhielt liegt bei dem von mir verwendeten Treiber PDO unter
PHP..

Rein zufällig, innerhalb einer Recherche zu einem anderen Thema, habe ich
erfahren, dass es diesen Bug seit der PHP-Version 5.6 gibt und zumindest
noch nicht bis zur Version 7.3 wieder aus der Welt geschafft wurde.

Viele Grüße
Hamburgo
Hamburgo
Beiträge: 87
Registriert: Di 28. Mai 2019, 17:28

Hallo zusammen,

nachdem ich den PDO-FireBird- gegen Interbase-Firebird-Treiber
ausgetauscht habe und nun keine Fehler-Meldung mehr bezüglich
des Doppelpunkts bekomme, feiere ich erste Erfolge mit meinem
"EXECUTE BLOCK".

Dieser Block funktioniert:

Code: Alles auswählen

EXECUTE BLOCK 
AS 
    DECLARE VARIABLE ID BI;
    DECLARE VARIABLE ZWECK XTXT;
    DECLARE VARIABLE LAST_ZWECK XTXT = '';

BEGIN
FOR 
SELECT 
            RID_BUCHUNGEN, 
            VERWENDUNGSZWECK 
    FROM 
            BUCHUNGEN
    WHERE
            RID_BUCHUNGEN > 0
    ORDER BY
            VERWENDUNGSZWECK ASC
    INTO    :ID, :ZWECK 
DO
BEGIN
        UPDATE
              BUCHUNGEN
        SET 
              EDITDATE         = '".date("Y-m-d H:i:s", time())."',
              VERWENDUNGSZWECK = SUBSTRING(TRIM(:ZWECK || ' R' || :ID) FROM 1 for 300)
        WHERE
              RID_BUCHUNGEN = :ID;
END
END;
Hat allerdings den Nachteil, dass er ALLE Datensätze anpasst, auch
die, wo es nicht erforderlich ist.

Dieser Block funktioniert nicht:

Code: Alles auswählen

EXECUTE BLOCK 
AS 
    DECLARE VARIABLE ID BI;
    DECLARE VARIABLE ZWECK XTXT;
    DECLARE VARIABLE LAST_ZWECK XTXT = '';

BEGIN
FOR 
SELECT 
            RID_BUCHUNGEN, 
            VERWENDUNGSZWECK 
    FROM 
            BUCHUNGEN
    WHERE
            RID_BUCHUNGEN > 0
    ORDER BY
            VERWENDUNGSZWECK ASC
    INTO    :ID, :ZWECK 
DO
BEGIN
    IF ( ZWECK = LAST_ZWECK ) THEN
        UPDATE
              BUCHUNGEN
        SET 
              EDITDATE         = '".$_SESSION['HeuteDb']." ".$_POST['Startzeit']."',
              VERWENDUNGSZWECK = SUBSTRING(TRIM(:ZWECK || ' R' || :ID) FROM 1 for 300)
        WHERE
              RID_BUCHUNGEN = :ID;
    END
    LAST_ZWECK = ZWECK;
END;
Da ist wohl noch ein Logik-Fehler drin !

Die IF-Abfrage nach "DO BEGIN" soll eigentlich dafür sorgen, dass ein
UPDATE nur bei Datensätzen gemacht wird, deren Verwendungszweck
gleich dem aus dem Datensatz DAVOR ist, also eine Dublette festgestellt
wird.
Klappt aber nicht. Es werden überhaupt keine Dubletten festgestellt,
was definitiv nicht stimmt. Knapp 30% aller Datensätze sind Dubletten.

Kann mir jemand sagen, wie das Ding aussehen muss, damit das klappt ?

Danke und viele Grüße
Hamburgo
Beiträge: 87
Registriert: Di 28. Mai 2019, 17:28

Hallo Volker,

jetzt erst habe ich gesehen, dass Du mir einen modifizierten
"EXECUTE BLOCK" hier eingestellt hast.

Der funktioniert, zumindest bei mir, NICHT, enthält wohl aber den entscheidenden
Tipp, der mir weitergeholfen hat. :D

Und zwar das "BEGIN" hinter dem "THEN" im IF-Statement !!!

Folgender Block funktioniert jetzt bei mir, zumindest in meiner Labor-DB:

Code: Alles auswählen

EXECUTE BLOCK 
AS 
     DECLARE VARIABLE ID BI;
     DECLARE VARIABLE ZWECK XTXT;
     DECLARE VARIABLE LAST_ZWECK XTXT = '';
BEGIN
     FOR SELECT 
                 RID_BUCHUNGEN, 
                 VERWENDUNGSZWECK 
         FROM 
                 BUCHUNGEN
         WHERE
                 RID_BUCHUNGEN > 0
         ORDER BY
                 VERWENDUNGSZWECK ASC
         INTO    :ID, :ZWECK 
      DO
        BEGIN
            IF (ZWECK = LAST_ZWECK) THEN BEGIN
                UPDATE
                      BUCHUNGEN
                SET 
                      EDITDATE         = '".date("Y-m-d H:i:s", time())."',
                      VERWENDUNGSZWECK = SUBSTRING(TRIM(:ZWECK || ' R' || :ID) FROM 1 for 300)
                WHERE
                      RID_BUCHUNGEN = :ID;
            END
            LAST_ZWECK = ZWECK;
        END
END
Das Ergebnis sieht auf den ersten Blick sehr vielversprechend aus.

Dafür meinen herzlichen Dank.

Jetzt werde ich das Ganze mal auf die Erfordernisse meiner Live-DB
adaptieren und natürlich berichten, ob ich auch dort ein vergleichbar
erfreuliches Ergebnis erhalte.

Viele Grüße
vr2
Beiträge: 141
Registriert: Fr 13. Apr 2018, 00:13

Hallo Hamburgo,
jetzt erst habe ich gesehen, dass Du mir einen modifizierten "EXECUTE BLOCK" hier eingestellt hast.
Ich hätte ihn nicht in Kleinbuchstaben umwandeln sollen ;)
Der funktioniert, zumindest bei mir, NICHT, enthält wohl aber den entscheidenden Tipp, der mir weitergeholfen hat. :D


Der muss auch bei Dir funktionieren, wenn er bei mir funktioniert, ich hab ihn getestet vorm Posten. Du musst allerdings die Spaltentypen int und varchar(100) oben bei den declares durch Deine ursprünglichen Domains BI und XTXT ersetzen, gut möglich, dass meine beiden Typen nicht zu Deiner Tabelle passen, zb weil Deine Buchungstexte länger sind, aber syntaktisch ist der execute block korrekt.
Und zwar das "BEGIN" hinter dem "THEN" im IF-Statement !!!
Eher das begin - end der for-select do-Schleife. Das fehlte. Das begin end fürs if brauchst Du eigentlich nicht, da das if selber klammert, aber ohne begin-end nur ein Statement. Da Du im if nur das update statement hast, würde das reichen ohne begin-end beim if. Klarer ist aber häufig ein begin end, auch wenn es nicht nötig ist.
Folgender Block funktioniert jetzt bei mir, zumindest in meiner Labor-DB: ...
Das Ergebnis sieht auf den ersten Blick sehr vielversprechend aus.
Dafür meinen herzlichen Dank.
Gerne. Es waren also nur zwei Sachen: Ein kaputter php-Treiber und im body der for-select Schleife fehlte die Aktualisierung der Vergleichsvariablen last_zweck.

Viele Grüße, Volker
Hamburgo
Beiträge: 87
Registriert: Di 28. Mai 2019, 17:28

Hallo Volker,

meine Feststellung, dass Dein "EXECUTE BLOCK" bei mir nicht funktioniert,
muss ich korrigieren.

Mir war beim Copy&Paste etwas (wohl durch unsauberes Tippen) reingerutscht,
was einen Fehler auslöste und ich in meinem ganzen Frust die Fehler-Meldung
nicht genau gelesen habe.

Ich sah nur das Orange und war mal wieder bedient.

Ich bitte um Entschuldigung.

So, ich habe mir nun nochmals meine 1. Version vom 30. Apr 2022, 13:29 raus-
geholt und mit Deiner abgeglichen.

Diese beiden Versionen unterscheiden sich an 3 Punkten:

1. Ich habe in der ORDER BY-Zeile am Ende ein ";" und das stört. Habe ich verstanden.
2. Ich habe in der Zuweiseungs-Zeile vor LAST_ZWECK einen ":". Warum der stört erschliesst sich mir nicht wirklich.
3. Am Ende des Blocks fehlt bei mir ein END. Auch das ist nachvollziehbar.

Der von mir erwähnte "BEGIN" hinter dem THEN hatte ich in meiner
1. Version zu meiner Überraschung ja schon und er ist mir wohl
zwischenzeitlich irgendwie abhanden gekommen.

Entgegen Deinen Ausführungen, ist er zumindest bei mir unbedingt erforderlich.

Entferne ich diesen BEGIN, dann wirft das zwar keine Fehler-Meldung, aber es werden
keine UPDATES durchgeführt.

Packe ich den BEGIN wieder rein, funktioniert alles bestens.

Der "EXECUTE BLOCK" erledigt die Aufgabe in ca. 15 Sekungen gegenüber 4 Minuten bei der Client-
Variante. Das ist echt cool.
vr2
Beiträge: 141
Registriert: Fr 13. Apr 2018, 00:13

Hamburgo hat geschrieben: Do 19. Mai 2022, 15:32 Der "EXECUTE BLOCK" erledigt die Aufgabe in ca. 15 Sekungen gegenüber 4 Minuten bei der Client-Variante. Das ist echt cool.
ja. Es lohnt sich, wenn Daten nicht zum Client transportiert werden müssen. Schön, dass es läuft.

Grüße, Volker
Antworten