String als Column Name

Hallo Leute

Habe folgendes Problem:

Ich versuche in einem SELECT Statement die Column Names variabel zu gestalten.

Nehmen wir an wir hätten eine Table “article” mit z.b. 2 columns “price_eur” und “price_usd”.

Zudem habe ich eine Table “contact” mit der column “currency”;

Ziel ist sowas wie:

SELECT 'price_' || (SELECT currency FROM contact WHERE id = 1) from article;

So könnte ich den price in der jeweiligen Währung des angegebenen Kontakts bekommen ohne dass ich die Query jemals umbauen müsste.

Leider bekomme ich dafür nur den string zurück, da Postgres so nicht weiss, das dieser string einen Spaltennamen repräsentiert. Quote ich die Begriffe jedoch nicht als string, kann ich sie nicht concatenaten.

Das Ergebnis ist also ungefähr so:
price_chf
price_chf
price_chf
price_chf
price_chf
price_chf
price_chf

Eine Idee von mir war, den string mit einem Cast als Column zu definieren. So was wie:

('price_' || 'eur')::column_name

Leider kenne ich den korrekten Datentyp nicht, wenns überhaupt so einen gibt.

Natürlich könnte ich das ganze auch mit plpgsql lösen, jedoch sehe ich nicht ein, weshalb man für so eine einfach query grad ne stored procedure bauen soll.


Hat irgendjemand eine Idee? :confused:

Wenn Dein Modell normalisiert wäre und der Preis in einer Tabelle “price” mit einer Spalte “currency” gespeichert würde, dann hättest Du das ganze Problem nicht - dann wäre es nämlich ein einfacher Join.


Natürlich könnte ich das ganze auch mit > plpgsql > lösen, jedoch sehe ich nicht ein, weshalb man für so eine einfach query grad ne stored procedure bauen soll.

Mit diesem Datenmodell geht es nicht anders.


Hat irgendjemand eine Idee? > :confused: >

Ein besseres Datenmodell!

Das geht nicht. Nicht mit SQL Das würde gehen mit z.B. plpgsql, aber auch dann nur als dynamisches Query, EXECUTE.

Der Grund ist simpel: Der Planner hat einen Plan zu erstellen. Das kann er nicht, wenn nicht schon vor Ausführung der Abfrage die Struktur des Ergebnisses feststehen würde.

In Kurz: vergiß es.

In Lang: überdenke Dein Design.

Andreas

Merci für die Antworten.

Bezüglich Datenmodell: Ich hätte vielleicht vorwegnehmen sollen, dass mir bewusst ist, dass mein Datenmodell jeglicher Normalisierung widerspricht und sehr unschön ist. Leider gibt es gute Gründe für mich dies so zu tun. Meine Applikation hat mittlerweile Dimensionen angenommen, welche Änderungen in der Basis SEHR aufwändig machen. Ich habe mir das Design lange überlegt und auch die normalisierte Variante bereits als Diagramm skizziert. Ich hatte die Wahl zwischen der edlen, aufwändigen oder der einfachen, dafür etwas hässlichen (und mindestens um den Faktor 10 schnelleren) Lösung. Es ist lediglich eine Zeitfrage.

Ich bin bereits daran, eine dynamische Query mit EXECUTE zusammenzubasteln. Werde meine Lösung hier posten wenn ich fertig bin (ich hasse Problem-Threads ohne Lösung ^^).

Vermuteter Faktor 10, oder hast Du das wirklich getestet?

Bezugnehmend auf deine Wahl zwischen Normalisierung oder nicht: nun stehst du vor dem Problem, Aufgaben lösen zu müssen, wie es in einer normalisierten Variante wohl nicht geben würde.

Vermuteter Faktor 10, oder hast Du das wirklich getestet?

Schwierig zu testen, dafür müsste ichs ja beinahe implementieren.

Der Faktor basiert auf Schätzungen, die ich anstellte, nachdem ich für beide Möglichkeiten Klassendigramme skizziert und die wichtigsten Relationen auch gleich als SQL Code implmentiert hatte.

Das Zusammenbauen der Relationen in SQL ist die eine Sache…der grosse Aufwand entsteht dann erst beim Anpassen des php-Codes. Stell dir eine Informatikbude mit eigenem CMS (in php) und ca. 10’000 Kunden vor. Das gesamte Buchhaltungskonzept war ursprünglich für eine Währung vorgesehen (etwas kurzsichtig … ich war auch nicht von Anfang an dabei). Änderungen in den fundamentalen Tabellen der Buchhaltung ziehen explosionsartigen Aufwand und Anpassungen des ganzen CMS nach sich. Mit anderen Worten: Mit der normalisierten Version müsste ich wirklich die gesamte Buchhaltung umbauen, da bliebe fast kein Stein auf dem anderen. Während diesen Umstellungen muss das Buchhaltungssystem auch noch nahtlos weiterfunktionieren.

Wir können mit grosser Sicherheit sagen, dass wir nie mehr Währungen als EUR, USD und GBP nutzen werden. Somit ist diese Sache langfristig geritzt. Ich möchte in Sachen Kurzsichtigkeit nicht die selben Fehler machen wie meine Vorgänger, aber ich denke, dass diese Lösung einen akzeptablen Kompromiss darstellt.

Mit der einfachen Version bin ich mit einigen Anpassungen über den Berg.

Bezugnehmend auf deine Wahl zwischen Normalisierung oder nicht: nun stehst du vor dem Problem, Aufgaben lösen zu müssen, wie es in einer normalisierten Variante wohl nicht geben würde.

Das stimmt so wohl und war auch zu erwarten. Bisher ist dies jedoch das einzige Problem, das sich mit dieser Variante stellt und es sieht nach einem in Kauf zu nehmenden Aufwand aus.

So.

Hier wäre mal meine Lösung. Wem’s zu dirty ist: Bitte die Augen zuhalten.

CREATE FUNCTION get_article_price_by_contact(i_article_id integer, i_contact_id integer) RETURNS numeric
    AS $$
DECLARE
        price numeric;
        currency_code varchar;
BEGIN
        currency_code := (SELECT code FROM currency WHERE id = (SELECT currency_id FROM contact WHERE id = i_contact_id));
                EXECUTE 'SELECT price_' || currency_code || ' FROM article WHERE id = ' || i_article_id INTO price;
        RETURN price;
END;
$$
    LANGUAGE plpgsql;

Nun kannn ich also mit einem einfachen

SELECT get_article_price_by_contact(1,1);

gemütlich den jeweiligen Preis in der richtigen Währung holen.

Für mich ist das Problem zufriedenstellend gelöst. Danke für die Diskussion.

Dafür hat man Entwickler-, Test-, Staging- und Produktivsysteme.
Auf einem Livesystem entwickelt man nicht. Niemals.


Wir können mit grosser Sicherheit sagen, dass wir nie mehr Währungen als EUR, USD und GBP nutzen werden.

Warten wir, bis der EUR an Wert verliert und die Chinesem mit den CNY oder die Russen mit dem RUB eine Rolle spielen.

Kann ich nur zustimmen. Trotzdem zeigt die Praxis, dass man auch mit dem besten Unit-Testing selten alle Fälle abdecken kann und leider unterscheiden sich Testumgebungen oft leicht von Livesystemen.
Wir sprechen hier davon, das Herzstück unseres CMS auszutauschen. Das wird definitiv nicht einfach über die Bühne gehen.

Warten wir, bis der EUR an Wert verliert und die Chinesem mit den CNY oder die Russen mit dem RUB eine Rolle spielen.

Na gut - stattgegeben ^^. Aber diese Erweiterungen wären danach Peanuts.

Verstehe ich nicht.

Wie kann man denn anhand eines Klassendiagramms (was per se mal nichts mit dem DB-Modell zu tun hat) und ein paar Relationen auf einen Faktor 10 schliessen?

Was genau meinst Du mit “Relationen in SQL Code implementieren”?
Eine Relation ist ein Begriff aus dem (logischen) ER-Modell, meistens (nicht immer) wird aus einer Relation eine Tabelle.
Eine Implementierung wäre dann ein CREATE TABLE.
Wie kann man daraus einen Faktor 10 ableiten?

Gut ich sehe, ich habe mich semantisch inkorrekt ausgedrückt. Da man Klassendiagramm wohl nur für Klassen verwendet (für was auch sonst ^^) trifft hier ER-Diagramm wohl eher zu.

Was genau meinst Du mit “Relationen in SQL Code implementieren”? Eine Implementierung wäre dann ein CREATE TABLE.

Genau. Plus Constraints, Views, neue Columns in anderen Tables etc.

Wie kann man daraus einen Faktor 10 ableiten?

Der geschätzte Faktor 10 ergibt sich aus den Auswirkungen der starken Änderungen des ER-Modells auf den eigentlichen Applikationscode (php). Der ist wie gesagt ziemlich umfangreich. Da ich euch den nicht posten kann, müsst ihr mir den Faktor 10 wohl oder übel mal einfach glauben. Ist auch nicht wirklich wichtig. Dieser Faktor soll ja nur zum Ausdruck bringen, dass der Aufwand erheblich grösser ist.

Ach so.
Ich hatte Deine Aussage (“und mindestens um den Faktor 10 schnelleren”) auf die Geschwindigkeit der Statements bezogen, nicht auf den Entwicklungsaufwand.

Den Entwicklungsaufwand kann man vermutlich daraus ableiten (den Wartungsaufwand auch? :wink: ).