Ich möchte bei jedem SELECT gegen eine Tabelle zusätzlich vor der Rückgabe eine einfache Funktion aufrufen, die ein refresh() der Tabelle macht. Das Refresh braucht es zwecks Synchronisation mit einer Sybase Datenbank.
Meine erste Idee war eine Regel zu schreiben, aber das klappt natürlich so nicht da das System dann in eine Endlosschleife geht.
CREATE RULE myrule AS ON SELECT TO tabelle DO
(refresh();
SELECT * FROM tabelle;
);
Das refresh() braucht max. 30ms. Eine andere Idee wäre einen Cronjob zu machen, welcher das refresh() alle 200ms macht.
Die Tabelle ändert sich bei einem SELECT ja nicht, sondern nur bei INSERT, UPDATE oder DELETE. Wie wäre es also mit einem TRIGGER, der bei diesen Ereignissen Dein refresh() macht?
Noch besser: Du stößt einen externen Prozeß an, via LISTEN/NOTIFY, via TRIGGER, wenn sich was ändert.
Die Tabelle sollte sich vor jedem SELECT updaten, wenn auf der Sybase Datenbank sich was geändert hat.
Das Problem ist, dass die Sybase Datenbank passiv ist und nicht auf die PostgreSQL Datenbank zugreifen kann. D.h. bevor ein SELECT aufgerufen wird, sollte eine Funktion in PostgreSQL bei der Sybase Datenbank nachschauen, ob sich etwas verändert hat, wenn ja, die Tabelle bei PostgreSQL updaten.
Zusatz:
Mit LISTEN/NOTIFY wäre es wahrscheinlich schön möglich, dass Sybase mit PostgreSQL kommuniziert. Das heisst Sybase schickt eine Meldung zu PostgreSQL, dass sich etwas verändert hat und dann kann auch eine Funktion gestartet werden. Ich schau mir das noch genauer an.
Vielen Dank für die Idee!
CREATE [ OR REPLACE ] RULE name AS ON event
TO table [ WHERE condition ] DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command … ) }
einfach das selekt in der rule weglassen … und stattdessen
DO ALSO … refresh()
außerdem:
ALSO ALSO indicates that the commands should be executed in addition to the original command.
If neither ALSO nor INSTEAD is specified, ALSO is the default.
also wie gesagt … das select auf die table weglassen, dann gibt es auch keine rekursion - was du so als “endlosschleife” bemerkst
wenn das natuerlich vor dem selekt passieren soll … jaja … dann wuerde ich versuchen mir eine view zu bauen … auf den deine anwendung schaut … du selektierst also den view → die rule macht ein refresh und einen selekt auf die eigentliche table … bei der du keine rule gesetzt hast
du baust dir eine funktion die ein setof <table%rowtype> zurueckliefert …
in dieser funktion rufst du zuerst das erwuenschte refresh() auf … und lieferst dann genau nichts zurueck …
als naechstes baust du dir ein view, das dann nichts weiter macht als ein
select * from mySetOftableX()
UNION
select * from tableX
liefert …
das view eben deshalb, dass du nicht in rekursive schwulidäd’n gerätst
mit ein wenig pech ist PG dumm genug die teil-selekts nacheinander auszufuehren …
We can improve performance by implementing a rule that will only refresh the materialized view if 1 second, 10 seconds, 1 minute, or 1 hour has passed since the last refresh and the data is being selected.
Aber eben, Jonathan möchte diese Materialized Views in PostgreSQL einbauen. Ich sehe keine Möglichkeit in SQL vor einem SELECT ein refresh() aufzurufen.
Meine Funktion ist in PL/Perl geschrieben und dort kann man mit RETURN SETOF type im Prinzip ein komplette Tabelle zurückgeben. Dann könnte man zum Beispiel folgendes ausführen:
select * from function();
Aber das Problem ist, dass es super langsam (fast eine Sekunde bei mir auf VMware) und CPU intensiv ist, wenn ich alle Werte auslesen muss, auch wenn ich ein statement habe wie
ja deshalb wollt ich das nicht empfehlen … daher auch die idee die funktion() mit dem refresh ne leere menge liefern zu lassen und nen union mit dem eigentlichen select auf die table - und das ganze union in ein view packen … AK meint das ginge nicht … ich habs noch nicht probiert
heul doch … ach ja … vielleicht wars ja ein heimeliches kompliment ?
und wenn ich schon mit potentiellen loesungen um mich werfe, sehe ich mich absolut nicht in der verpflichtung es selbst zu probieren … immerhin ist es mit arbeit verbunden … das kann der fragesteller auch selbst tun, wenn er die idee fuer prüfungs-wuerdig haelt … wenn nicht - dann ist das sein ding
Dann schreib Dir ein refresh(id), und nutze den Parameter gleich für das Select, um nur das zu selecten, was gesucht ist. Du könntest ja auch einen String mit einer where-Bedingung übergeben und ein dynamisches SQL basteln.
naja … ich bin ja auch neugierig … hier meine ersten forschungs ergebnisse:
CREATE OR REPLACE FUNCTION refresh()
RETURNS SETOF atable AS
$BODY$
BEGIN
END;
$BODY$
LANGUAGE 'plpgsql' [color=Red]IMMUTABLE[/color] <-- falscher fehler .. muss heissen VOLATILE damit der Optimizer nicht auf die Idee kommt es wegzulassen :)
COST 10 ROWS 1;
explain analyse
select * from refresh()
union
select * from atable
wenn er allerdings die beiden geschichten parallel abarbeitet waere das kein gutes ding …
dann koennte man allerdings versuchen das refresh() in einer subquery unterzubringen
symbiont,
Ich finde Deine Idee mit dem UNION im Prinzip super und habe ein paar Tests gemacht. Leider ist das
SELECT * FROM tabelle
immer schneller als die Funktion. D.h. erst beim zweiten Aufruf zeigt es die richtigen Werte an. Das ändert auch nicht, wenn ich die Reihenfolge vertausche.
Was kann ich mit einem subquery anstellen um das Problem zu verhindern? Anscheinend werden beide parallel abgearbeitet wie Du vermutet hast.