Tabelle als Variable abfragen

Hallo zusammen,

ich bin auf diesem Gebiet relativ frisch unterwegs und alles andere als ein Fachmann.
Hier mein Problem:

Ich möchte zwei Daten (Datum) von einander abziehen um die zeitliche Differenz zu bestimmen. Das klappt auch wunderbar.
Jetzt ist es aber so, dass ich die aktuelle Tabelle in einer Variablen vorliegen habe. Wie bekomme ich in meiner Abfrage das so hin, dass ich das Schema und die Variable verknüpfe?

Beispiel:
select current_date - max(Spalte) from schema.tabelle where …value into variable;

Das fett dargestellte tabelle ist eine Variable. Ich habe alles abgesucht. Vielleicht auch falsch gesucht, jedoch finde ich nichts hilfreiches dazu.

Danke vorab.

Gruß
Nexx

wo liegt diese Variable vor, in Deiner Applikation oder schon in der datenbank?

Falls letzteres: https://www.postgresql.org/docs/12/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN

Die Variable wird erst in der Abfrage gefüllt.

Ja, dann wirst das als dynamisches Statement ausführen müssen.

Alternativ das derzeitige Konzept prüfen. Warum meinst Du, daß das so sein muß?

Das ist wirklich mein erstes Projekt in diesem Bereich.

Das Ganze liegt als Oracle Programmierung fertig vor.
Ich muss das in Postgresql migrieren.

Daher will ich bei diesem Programm nicht das ganze Konzept ändern.

Wie sieht denn ein dynamisches Statement aus, wenn das Schema feststeht, die Tabelle jedoch als Variable vorliegt?

schau dir einfach die Beispiele in dem Link an, den ich Dir schon geschickt habe.

Warum zeigst Du uns nicht einfach mal den PL/SQL Code der Oracle Prozedur? Dann müssten wir nicht raten, was genau da passieren soll.

Hier ist der Quellcode. Das wird als Funktion aufgerufen und bekommt vier Werte übergeben (tab_name, tab_id_name, tab_id_value, hist_cause).
Danke euch.

Mit den Informationen aus deinem Link komme ich nicht weiter.

DECLARE
tmpsql character varying(2000);
tmpcols_tabold character varying(1000);
tmpcols_tabnew character varying(1000);
icount integer;
vtab_hist character varying(30);
itimespan integer;
ielapsedtime integer;

BEGIN
select count(*) into icount from awb.hist_config where table_name = upper(tab_name);

IF icount <> 1 THEN
RAISE EXCEPTION ‘!! Kein Hist-Tabellenname in der Taballe HIST_CONFIG angegeben !!’;
return;
ELSE
select table_hist, delete_time into vtab_hist, itimespan from awb.hist_config where table_name = upper(tab_name);
END IF;

vtab_hist = lower(vtab_hist);

select count(*) into icount from information_schema.tables where table_name = (vtab_hist) and table_schema = ‘awb’;

IF (hist_cause <> ‘D’) THEN
select count() from information_schema.tables where table_name = (vtab_hist) and table_schema = ‘awb’ into icount;
IF (icount > 0) THEN
select current_date - max(hist_date) from vtab_hist where schemaname = ‘awb’ and tab_id_name = tab_id_value into ielapsedtime;
IF (ielapsedtime
86400 < itimespan) THEN
return;
END IF;
END IF;
END IF;
END;

Quellcode des betroffenen Abschnittes der Oracle DB

if(hist_CAUSE <> ‘D’) then
EXECUTE IMMEDIATE 'select count() from ’ || vtab_Hist || ’ where ’ || tab_ID_NAME || ’ = ‘’’ || tab_ID_VALUE || ‘’’’ into iCount ;
if (iCount > 0) then
EXECUTE IMMEDIATE ‘select SYSDATE-MAX(HIST_DATE) from ’ || vtab_Hist|| ’ where ’ || tab_ID_NAME || ’ = ‘’’ || tab_ID_VALUE || ‘’’’ into iElapsedTime;
if(iElapsedTime
86400 < iTimeSpan) then
return;
end if;
end if;
end if;

Nun, du bekommst ja schon tab_name übergeben. In deinen EXECUTE-statements siehst auch wie das dynamisch zusammengestzt wird, das kannst Du direkt als Vorlage nehmen. Dazu dann das durchlesen, was ich Dir als Link geschickt habe.

Vielen Dank für die Hilfe. Ich muss jetzt los. Schaue mir das morgen früh weiter an. Vielleicht bekomme ich es dann hin.

Dynamisches SQL macht man in Postgres am besten mit format() dann bleibt das eigentlich SQL noch lesbar und mit den passenden Platzhaltern muss man sich auch nicht um das Quoting kümmern. Parameter übergibt man auch als solche, die sollte man nicht in den SQL String “kopieren” (das war aber auch schon unter Oracle eine schlechte Idee).

Basierend auf Deinem Code, würde ich mal vermuten, dass Du sowas hier suchst:

DECLARE
  tmpsql character varying(2000);
  tmpcols_tabold character varying(1000);
  tmpcols_tabnew character varying(1000);
  icount integer;
  vtab_hist character varying(30);
  itimespan integer;
  ielapsedtime integer;
  
BEGIN
  select count(*) 
     into icount 
  from awb.hist_config where 
  table_name = upper(tab_name);

  IF icount <> 1 THEN
    RAISE EXCEPTION '!! Kein Hist-Tabellenname in der Taballe HIST_CONFIG angegeben !!';
    return;
  ELSE
    select table_hist, delete_time 
        into vtab_hist, itimespan 
    from awb.hist_config 
    where table_name = upper(tab_name);
  END IF;

  vtab_hist := lower(vtab_hist);

  select count(*) 
    into icount 
  from information_schema.tables 
  where table_name = vtab_hist
    and table_schema = 'awb';

  IF (hist_cause <> 'D') THEN
  
    execute format('select count(*) from %I where %I = $1', vtab_hist, tab_id_name) 
      into icount 
     using tab_id_value; --<< übergibt den Wert für Parameter $1
    
    IF (icount > 0) THEN
      execute format('select current_date - max(hist_date::date) from %I where %I = $1', vtab_hist, tab_id_name)
        into ielapsedtime
      using tab_id_value;
      
      IF (ielapsedtime*86400 < itimespan) THEN
        return;
      END IF;
      ....
    END IF;
  END IF;
END;

Der Platzhalter %I für die format() Funktion setzt an der Stelle einen “Identifier” ein (Tabellen- bzw. Spaltennamen). Das $1 ist ein Parameter Platzhalter für das EXECUTE. Der Parameter wird dann via USING beim EXECUTE übergeben.

Format ist wahrscheinlich eine vertretbare Lösung. Die Frage wäre ja, was der Schaden sein könnte, wenn man falsche, ungeplante Werte (tabellennamen) reinschmuggelt. Die Antwort ist hier übersichtlich.

Die Frage wäre aber auch, was der reale Anwendungsfall ist. Warum sind es wechselnder History Tabellen und wieviel können es denn eigentlich in der Realität sein? 2 oder 3? Oder 50 oder N>50? Also warum gibt es diesen Sch…?
Pseudo Partitioning? Pseudo Staging? Oder einfach nur eine Krücke für Developer Tests?
In dem Zusammenhang: Warum ist das eine headless Operation? Und wenn es das ist, kann man die Tabelle nicht schon vorher mit ein wenig “bashing” danymisch ändern?

Jenachdem vielleicht doch lieber alles in geordnete Bahnen* lenken, statt das weiter mitzuschleifen!

*Eine Dimension in der Logtabelle spendieren für verschiedene Umgebungen, Testszenarien oder was auch immer der Trigger für die Varianz ist.

Dann verwende einen weiteren Platzhalter. Wenn Du eine Variable “table_schema” hast in der das Schema drinsteht, geht das so:

execute format('select count(*) from %I.%I where %I = $1', table_schema, vtab_hist, tab_id_name)
...

Ok also es gibt 30 (mögliche) Historie Tabellen?!

Meine Fragen gehen in verschiedene Richtungen.
“Developer Kram” wurde mal eingebaut, um was zu testen, es wird aber produktiv nicht genutzt, also wird aus 30 Tabellen 1, Problem erledigt.

Dann aus fachlicher Sicht: Was ist der Sinn einer Historie Tabelle? […Bedenkzeit…]
Was ist der Sinn von 30 Historietabellen? […Bedenkzeit…]
Wenn der Mechanismus dazu da ist, unterschiedliche “Wirklichkeiten” bzw. eben “Historien” eines gegebenen Status Quo zu liefern, ist es einfach sehr fraglich. Wenn es für verschiedene Umgebungen genutzt wird, ist es verständich (dev, stage, prod) und kann vielleicht anders gelöst werden (30 dev stage prod sind aber ungewöhnlich…)
30 Historietabellen für 30 verschiedene Muttertabellen wäre wiederum was anderes.

“Headless”
Du schreibst, es sei eine Funktion. Was Du zeigst ist aber keine Funktion. Es ist ein anonymer Block. Der ist nur aufrufbar

  • interaktiv per command line oder IDE (wo spontan geändert werden könnte)
  • aus einem Clientprogramm heraus (wo er zusammengesetzt werden könnte)
  • aus dem Betriebssystem (wo er zusammengesetzt werden könnte)

in allen Fällen ist der Code, der ausgeführt wird, eigentlich variabel und kann schon vor der Ausführung so “geschrieben” werden, dass die richtige Tabelle verwendet wird, statt sie erst im Code zu tauschen.

Wie gesagt, Format bzw. der Code von Castorp sind sicher eine gute Lösung, wenn Deine und meine (Fern)Sicht, nicht fundiert genug sind.

Danke mal für eure Hilfe, habe auch nach einer Antwort dazu gesucht! :slight_smile: