patternsqlMinor
Efficiently comparing prices in different currencies
Viewed 0 times
efficientlycomparingdifferentpricescurrencies
Problem
I want to make it possible for user to search products within a price range. User should be able to use any currency (USD, EUR, GBP, JPY, ...), no matter what currency is set by the product. So, the product price is 200USD and, if the user searches the products that costs 100EUR - 200EUR, he still may find it. How to make it fast and effective?
Here is what I have done until now. I store the
If user is searching with other currency, let's say USD, we calculate the price in EUR and search the
In this way we can compare prices very fast, because we don't need to calculate the actual price for every row, because it's calculated once.
Bad thing is that at least every day we have to re-calculate the
Is there a better way to do deal with this?
Isn't there any other clever solution? Maybe some mathematical formula? I have an id
Here is what I have done until now. I store the
price, currency code and the calculated_price that is the price in Euros (EUR) that is the default currency.CREATE TABLE "products" (
"id" serial,
"price" numeric NOT NULL,
"currency" char(3),
"calculated_price" numeric NOT NULL,
CONSTRAINT "products_id_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "currencies" (
"id" char(3) NOT NULL,
"modified" timestamp NOT NULL,
"is_default" boolean NOT NULL DEFAULT 'f',
"value" numeric NOT NULL, -- ratio additional to the default currency
CONSTRAINT "currencies_id_pkey" PRIMARY KEY ("id")
);
INSERT INTO "currencies" (id, modified, is_default, value)
VALUES
('EUR', '2012-05-17 11:38:45', 't', 1.0),
('USD', '2012-05-17 11:38:45', 'f', '1.2724'),
('GBP', '2012-05-17 11:38:45', 'f', '0.8005');
INSERT INTO "products" (price, currency, calculated_price)
SELECT 200.0 AS price, 'USD' AS currency, (200.0 / value) AS calculated_price
FROM "currencies" WHERE id = 'USD';If user is searching with other currency, let's say USD, we calculate the price in EUR and search the
calculated_price column.SELECT * FROM "products" WHERE calculated_price > 100.0 AND calculated_price < 200.0;In this way we can compare prices very fast, because we don't need to calculate the actual price for every row, because it's calculated once.
Bad thing is that at least every day we have to re-calculate the
default_price for all rows, because the currency rates has been changed.Is there a better way to do deal with this?
Isn't there any other clever solution? Maybe some mathematical formula? I have an id
Solution
Here's a different approach for which recomputing the
Suppose that in the
To quickly retrieve a set of products with a price point between, say, 50 USD and 100 USD that include the desired results, you could do something like that:
where
Since the rates change only slightly over short periods of time, the above query is likely to give a close approximation of the final result. To get the final result, let's filter out the products for which the prices have slipped out of the bounds due to the changes in rates since the last update of
where
The efficiency comes from the fact that the range of rates is supposed to be small, the values being close together.
calculated_price is just an optimization, as opposed to being strictly necessary.Suppose that in the
currencies tables, you add another column, last_rate, which contains the exchange rate at the time the calculated_price was last updated, no matter when this happened.To quickly retrieve a set of products with a price point between, say, 50 USD and 100 USD that include the desired results, you could do something like that:
SELECT * FROM products
WHERE calculated_price > 50.0/(:last_rate*
(SELECT coalesce(max(value/last_rate),1) FROM currencies
WHERE value>last_rate))
AND calculated_price < 100.0/ (:last_rate*
(SELECT coalesce(min(value/last_rate),1) FROM currencies
WHERE value<last_rate))where
:last_rate contains the EUR/USD exchange rate at the time of the last update. The idea is to increase the interval to take into account the maximum variation of every currency. The increase factors for both ends of the interval are constant between rates updates, so they could be pre-computed.Since the rates change only slightly over short periods of time, the above query is likely to give a close approximation of the final result. To get the final result, let's filter out the products for which the prices have slipped out of the bounds due to the changes in rates since the last update of
calculated_price:WITH p AS (
SELECT * FROM products
WHERE calculated_price > 50.0/(:last_rate*
(SELECT coalesce(max(value/last_rate),1) FROM currencies
WHERE value>last_rate))
AND calculated_price 50/:current_rate
AND price/c.value<100/:current_rate;where
:current_rate is the more up-to-date rate with EUR for the money choosen by the user.The efficiency comes from the fact that the range of rates is supposed to be small, the values being close together.
Code Snippets
SELECT * FROM products
WHERE calculated_price > 50.0/(:last_rate*
(SELECT coalesce(max(value/last_rate),1) FROM currencies
WHERE value>last_rate))
AND calculated_price < 100.0/ (:last_rate*
(SELECT coalesce(min(value/last_rate),1) FROM currencies
WHERE value<last_rate))WITH p AS (
SELECT * FROM products
WHERE calculated_price > 50.0/(:last_rate*
(SELECT coalesce(max(value/last_rate),1) FROM currencies
WHERE value>last_rate))
AND calculated_price < 100.0/ (:last_rate*
(SELECT coalesce(min(value/last_rate),1) FROM currencies
WHERE value<last_rate))
)
SELECT price,c.value FROM p join currencies c on (p.currency=c.id)
WHERE price/c.value>50/:current_rate
AND price/c.value<100/:current_rate;Context
StackExchange Database Administrators Q#28010, answer score: 4
Revisions (0)
No revisions yet.