Währungen umrechnen

Tommy57

Erfahrenes Mitglied
Hallo,

ich stehe gerade wieder mal vor einem Problem.

Wir wollen in unserer Applikation die Währungen wechseln können. Hierfür haben wir eine Tabelle mit den jeweiligen Wechselkursen (currency, date, rate).

Mein SQL ist ziemlich mies. Vielleicht kann mir jemand sagen, ob es so etwas wie Funktionen gibt, damit ich vllt so etwas schreiben kann:

Code:
SELECT
    SUM (amount * price * getRate('$', date)) as totalprice
FROM
    carsells
WHERE
   id = 17

Das Problem ist, dass ich gerade ziemlich viele Statements umschreiben muss, wo ich immer den Umweg über die Währungstabelle machen muss und mit meinen SQL Fähigkeiten ist das nicht gerade mit einer Zeile gemacht, sondern würde so aussehen (ungetestet):

Code:
SELECT
    SUM (b1.amount * b1.price * b1.rate) AS totalprice
FROM
    (SELECT
        a1.amount,
        a1.price,
        MAX(a2.rate) AS rate
    FROM
        (SELECT
            sells.amount,
            sells.price,
            MAX(cur.date) AS curdate
        FROM
            (SELECT
                amount,
                price,
                date
            FROM
                carsells
            WHERE
                id = 17) sells
        INNER JOIN
            currency cur
        ON
            cur.date <= sells.date
        GROUP BY
            sells.amount,
            sells.price,
            sells.date AS selldate) a1
    INNER JOIN
        currency cur a2
    ON
        a2.date = a1.curdate
    GROUP BY
        a1.amount,
        a1.price) b1

Das Statement ist ziemlich unübersichtlich, und zusätzlich verliere ich glaube ich alle Zeilen, wo am gleichen Tag die gleiche Anzahl mehrfach verkauft wurde. Das MAX ist da drinnen, falls mal fälschlicherweise eine Zeile in der Währungstabelle doppelt existiert.

Kann mir vllt jemand sagen, wie man dieses Problem besser lösen könnte, als jetzt alle Statements so heftig umzuschreiben?

Gruß, Tommy
 
Super danke.


Ich habe mir da schon etwas zusammengeschustert. Das Ganze ist noch nicht korrekt, aber das innere Statement (zwischen BEGIN und END) ist schon fertig.

Muss mich da noch etwas weiter einlesen. Ist das denn soweit schon mal okay?

Code:
DELIMITER $$
CREATE PROCEDURE exchangePrice(
        IN 
            s_currency INT,
            s_date DATE,
            s_price AS DECIMAL(14,2),
       OUT newprice DECIMAL(14,2))
    BEGIN
       (SELECT
            (s_price * p3.rate) INTO newprice
        FROM
            (SELECT
                MAX(p2.rate) AS rate
            FROM
                (SELECT
                        MAX(cur.date) AS curdate
                    FROM
                        currency cur
                    WHERE
                        currency = s_currency
                AND
                    cur.date <= s_date) p1
            INNER JOIN
                currency_history p2
            ON
                currency = s_currency
            AND
                p2.date = p1.curdate) p3)
    END$$
 DELIMITER ;

Gruß, Tommy

EDIT: Statement angepasst
 
Zuletzt bearbeitet:
Item: Ein SQL ist selten in einer Zeile gemacht. Und wenn, dann ists selten lesbar. Dein Query ist sauber formatiert, da kann sowas auch länger werden (ist aber immer noch lesbar!)

item: Eine Funktion (Stored Procedure) zu schreiben, davon rate ich dir in dem Fall ab. Wenn du Grosse Datenmengen hast, dann wird für jede Zeile die Funktion aufgerufen und das darin liegende Select aufgerufen. bei 100'000 Zeilen werden also 100'000 SELECT gegen die DB gesetzt um jeweils den aktuellen Kurs auszuwählen. Also besser das ganze in dein normales SQL einbauen. Ist mit grosser Wahrscheinlichkeit performanter

item: Du kannst eine View erstellen. Ansätze um den aktuellsten Kurs auszuwählen findest du hier MySQL Aktuelle Einträge pro Gruppe auslesen
Das Problem mit der View ist, dass du dann IMMER den aktuellesten Kurs (Datum Jetzt) hast. Aber wenn ud den aktuellsten Kurs auf ein bestimmtes Datum haben willst dan geht das auch nicht. Wie ich in deinem SQL-Versuch sehe nimmst du das Datum aus der Tabelle carsells. Ergo ist das auch keine Lösung.

item: Bleibt nur noch alles in ein SQL zu quetschen. Ist aber auch nicht so schwer.
Annahme: Da das Feld auf das du filterst id heisst, geh ich mal davon aus dass dieses in der Tabelle carsells auch Eindutig ist. Ergo mit der id=17 kommt genau eine Zeile aus carsells

Wie fast immer, ungetestet
SQL:
SELECT
    car2.amount * car2.price * cur2.rate as totalprice
FROM
    (SELECT
        car1.id,
        cur1.currency,
        MAX(cur1.date) AS cur_date
    FROM
        currency cur1,
        carsells car1
    WHERE
        cur1.date <= car1.date
        -- Hier die Bedinungen einmal reinschreiben
        AND cur1.currency = '$'
        AND cur1.id = 17
    GROUP BY
        cur1.currency
        car.id) ids,
    currency cur2,
    carsells car2
WHERE
    cur2.currency = ids.currency
    AND cur2.date = ids.cur_date
    AND car2.id = ids.id
 
Zuletzt bearbeitet von einem Moderator:
Hallo Yaslaw,

bei 100'000 Zeilen werden also 100'000 SELECT gegen die DB gesetzt

Das hatte ich auch schon überlegt, ob das passiert.

Die Aufgabe ist eigentlich recht simpel. Es gibt in der Verkaufstabelle eine Spalte price und date. Aus den beiden Spalten muss ich nun irgendwie den Wechselkurs, also rate, generieren, so dass mir danach eine weitere Spalte rate zur Verfügung steht.

Daher war meine Frage auch, ob das nicht irgendwie einfacher geht, weil ich einige Statements habe, die schon extrem lang sind und die dann nochmal für die Währungen so umfangreich anzupassen, kann ziemlich viel Kraft kosten.

Ich werde das gleich mal alles testen und schauen, wie ich das am Besten gelöst kriege.

Danke auf jeden Fall für eure Hilfe.

Gruß, Tommy
 
Okay, also das mit dem Stored Procedure hat sich erledigt. Wenn ich nur über 100 Zeilen da dran gehe, braucht er schon 2 Sekunden. Das komplette Statement habe ich nach 100 Sekunden Laufzeit abgebrochen. Ohne den Währungswechsel braucht das Statement nur etwa 100ms.

Wenn es keine andere Lösung gibt, muss ich wohl echt jedes einzelne Statement anpassen. Das ist echt ärgerlich.

Gruß, Tommy
 
Ist meine Annahme richtig, das das Max-Date abhängig von einem Datum in einer anderen Tabelle ist?
Oder ist es IMMER der neuste Umrechnungskurs den du haben willst?
 
Hi Yaslaw,

das war falsch von mir, das sollte eigentlich ein LIMIT 1 sein. Als Vorsichtsmaßnahme, um die Ergebnismenge auf 1 zu setzen.

Gruß, Tommy
 
Also immer nur 1 rate pro Währung und davon das aktuellste.

So bietet sich eine View an:
SQL:
CREATE OR REPLACE VIEW vw_currency AS
SELECT
    cur2.currency,
    cur2.rate
FROM
    (SELECT
        cur1.currency,
        MAX(cur1.date) AS cur_date
    FROM
        currency cur1    
    GROUP BY
        cur1.currency) scur,
    currency cur2
WHERE
    scur.currency = cur2.currency
    AND scur.cur_date = cur2.date

Dann ist dein obiges SQL um nur 2 Zeilen erweitert
SQL:
SELECT
    car.amount * car.price * IFNULL(cur.rate ,1) as totalprice
FROM
    carsells car
    LEFT JOIN vw_currency cur ON car.currency = cur.currency
WHERE
    cur.currency = '$'
    AND car.id = 17
 
Zuletzt bearbeitet von einem Moderator:
Hi Yaslaw,

also nicht der aktuellste Wert, sondern der letzte Wert vor einem bestimmten Datum. Wenn zum Beispiel am 03.06.0212 kein Rate eingetragen ist, nehme ich den letzen Wert davor, also am 02.06.2012 oder 01.06.2012.

Funktioniert das mit Views?
 

Neue Beiträge

Zurück