FEHLER: Funktion pg_catalog.substring(double precision, integer, integer) existiert nicht

Hallo, ich habe eine PostgrSQL-Datenbank von einen alten Server 2003 auf einen Server 2016 umgezogen.
Backup und Restore der Datenbank habe ich jeweils mit PGAdmin des neuen Server gemacht.(Neue Version 9.5.5…alte Version war 8.1.8)
Die Anwendung (ein QM-System) läuft auch soweit. Nur bei einer wichtigen Funktion bekomme ich folgenden Fehler:
"
FEHLER: Funktion pg_catalog.substring(double precision, integer, integer) existiert nicht LINE 1: SELECT ‘R’ || SUBSTRING(EXTRACT( YEAR FROM CURRENT_TIMESTAMP… ^ HINT: Keine Funktion stimmt mit dem angegebenen Namen und den Argumenttypen überein. Sie müssen möglicherweise ausdrückliche Typumwandlungen hinzufügen. QUERY: SELECT ‘R’ || SUBSTRING(EXTRACT( YEAR FROM CURRENT_TIMESTAMP) from 3 for 2) || repeat(‘0’,5-length(rec.anzahl::integer + 1)) || rec.anzahl::integer + 1 CONTEXT: PL/pgSQL-Funktion next_rms() Zeile 6 bei RETURN
INSERT INTO…FEHLER: aktuelle Transaktion wurde abgebrochen, Befehle werden bis zum Ende der Transaktion ignoriert
SELECT MAX(reklamationsnummer) AS reklamationsnummer FROM rms WHERE ersteller = ‘44’ “”

Kann mir hier jemand weiterhelfen?
Vielen Dank schon mal für Eure Hilfe.

EXTRACT liefert ein ‘double precision’, das SUBSTRING erwartet aber da einen String. Vor 8.2 oder 8.3 wurde sowas automatisch gecastet, seit dem ist das nicht mehr der Fall. Ändere das also auf

SELECT 'R' || SUBSTRING(EXTRACT( YEAR FROM CURRENT_TIMESTAMP)::text ...

8.1 war übrigens seit November 2010 außer Support, in 9.5 ist die aktuelle Version 9.5.20. Warum tut man sich an, JAHRE hinter den aktuellen Versionen zu sein?

Als Erläuterung zu dem was akretschmer geschrieben hat: 9.5 hat ca. noch ein Jahr “zu leben”, dann gehört es auch zum alten Eisen.

Details dazu gibt’s hier: https://www.postgresql.org/support/versioning/

Wenn man in 2020 ein Migration angeht, sollte man mindestens auf 11, besser auf 12 wechseln.

Ich würde ja extract() in diesem Fall gar nicht verwenden, sondern einfach mittels to_char() formatieren. Auch das Formatieren einer Zahl mit führenden Nullen ist mit to_char() einfacher zu machen:

Der gesamte Ausdruck kann also zu folgendem vereinfacht werden:

SELECT 'R' || to_char(CURRENT_TIMESTAMP, 'yy')||to_char(rec.anzahl::integer + 1, 'FM00000')

Vielen Dank für die schnellen Antworten. Leider weiß ich nicht, wo ich ändern soll. Den Fehler bekomme ich ja, wenn ich eine neue Reklamation in unser QM-System eingebe. Sprich im Browser wird ein php-Script gestartet, welches mir diesen Fehler dann ausspuckt. Muss ich den Code direkt in der Postgresql-Datenbank eingeben? In den php-Dateien finde ich keinen entsprechenden Ausdruck?
Danke!

Der Browser startet kein PHP-Script, PHP wird auf dem Webserver ausgeführt. Und ja, die Befehle, die die DB ausführt, kommen von der Applikation, hier dann wohl das PHP.

Du solltest Dich mit dem Hersteller der Applikation in Verbindung setzen.

Unser System ist leider schon so alt, dass es den Hersteller nicht mehr gibt. Findet man hier einen Spezialisten, der sich eine Anpassung zutraut? Darf man das Forum überhaupt für die Suche nach einen Spezialisten benutzen?

Grundsätzlich kannst/darfst Du hier fragen. Ich vermute aber mal, dass Du hier nur wenig Erfolg haben wirst, weil PHP Experten unter den Postgres Experten wohl eher dünn gesät sein dürften.

Das richtige Unterforum dazu dürfte wohl: https://www.pg-forum.de/viewforum.php?f=70 sein

Darf ich nochmal auf mein Problem zurück kommen? Ich habe in eine Backup.sql einen entsprechenen String gefunden. Ich könnte doch versuchen im PSQL-Admin dieses Kommando per Hand einzugeben. Dann mit der “neuen” richtigen Syntax.
In der Backup-Datei habe ich diesen String:


CREATE FUNCTION next_rms() RETURNS character varying
AS $$
DECLARE
rec RECORD;
BEGIN
SELECT INTO rec COALESCE(MAX(SUBSTRING(reklamationsnummer from 4 for 5)),‘0’) AS anzahl FROM reklamationsmanagement.“rms” WHERE EXTRACT( YEAR FROM erstelldatum) = EXTRACT(YEAR FROM CURRENT_TIMESTAMP);
RETURN ‘R’ || SUBSTRING(EXTRACT( YEAR FROM CURRENT_TIMESTAMP) from 3 for 2) || repeat(‘0’,5-length(rec.anzahl::integer + 1)) || rec.anzahl::integer + 1;
END;
$$
LANGUAGE plpgsql;


ALTER FUNCTION reklamationsmanagement.next_rms() OWNER TO ortmaier;


Wie müsste ich diesen denn richtig eingeben? Vielen Dank schon mal.

Ich würde das so umschreiben:

CREATE OR REPLACE FUNCTION next_rms() 
  RETURNS character varying
AS $$
DECLARE
  l_anzahl varchar;
BEGIN
  SELECT COALESCE(MAX(SUBSTRING(reklamationsnummer from 4 for 5)),'0')
    INTO l_anzahl
  FROM reklamationsmanagement."rms" 
  WHERE EXTRACT(YEAR FROM erstelldatum) = EXTRACT(YEAR FROM CURRENT_TIMESTAMP);
  
  RETURN 'R' || to_char(CURRENT_TIMESTAMP, 'yy')||to_char(l_anzahl::integer + 1, 'FM00000')
END;
$$
LANGUAGE plpgsql;

Den Befehl dann im SQL client Deiner Wahl (psql, pgAdmin, …) ausführen.
Vielleicht legst Du die neue Funktion zuerstmal mit dem Namen next_rms_neu() an, und vergleichst das Ergebnis der neuen Funktion mit der alten - nur um sicher zu gehen. Danach kannst Du die alte ja löschen und die neue umbenennen.

Ich weiß Du hast die Funktion nicht geschrieben, aber das System scheint von Leuten programmiert worden zu sein, die sich nicht wirklich gut mit relationalen Datenbank auskennen. Neben der Tatsache, dass man eine Anzahl nicht als Teilstring in einer Spalte “versteckt”, gibt es praktisch keine schlechtere Lösung als das “max() + 1” Anti-Pattern um “Nummern” zu generieren (diese Funktion funktioniert vor allem nicht korrekt wenn das von zwei Transaktionen gleichzeitig ausgeführt wird.

Ändern dieser Funktion hat geholfen.
Bin begeistert. Vielen Dank