দক্ষতার সাথে বিভিন্ন মুদ্রায় দামের তুলনা করা


10

আমি ব্যবহারকারীর পক্ষে দামের মধ্যে পণ্য অনুসন্ধান করা সম্ভব করে তুলতে চাই। ব্যবহারকারীর যেকোন মুদ্রা (ইউএসডি, ইইউ, জিবিপি, জেপিওয়াই, ...) ব্যবহার করতে সক্ষম হওয়া উচিত, পণ্য দ্বারা কোন মুদ্রা সেট করা হোক না কেন। সুতরাং, পণ্যের দাম 200 ইউএসডি এবং যদি ব্যবহারকারী 100EUR - 200EUR দামের পণ্যগুলি অনুসন্ধান করে তবে সে এটি খুঁজে পেতে পারে। কীভাবে এটি দ্রুত এবং কার্যকর করা যায়?

আমি এখন পর্যন্ত যা করেছি তা এখানে Here আমি দোকানে price, currency codeএবং calculated_priceইউরো (EUR) এ মূল্য ডিফল্ট মুদ্রা যে হয়।

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';

যদি ব্যবহারকারী অন্য মুদ্রার সাথে অনুসন্ধান করে থাকে তবে আসুন ইউএসডি বলুন, আমরা ইউরোতে দাম গণনা করি এবং calculated_priceকলামটি অনুসন্ধান করি ।

SELECT * FROM "products" WHERE calculated_price > 100.0 AND calculated_price < 200.0;

এইভাবে আমরা দামগুলি খুব দ্রুত তুলনা করতে পারি, কারণ আমাদের প্রতিটি সারির জন্য প্রকৃত দাম গণনা করার দরকার নেই, কারণ এটি একবার গণনা করা হয়।

খারাপ জিনিস হ'ল কমপক্ষে প্রতিদিন আমাদের default_priceসমস্ত সারিগুলির জন্য পুনরায় গণনা করতে হবে, কারণ মুদ্রার হার পরিবর্তন করা হয়েছে।

এটি মোকাবেলা করার আরও ভাল উপায় আছে?

অন্য কোন চতুর সমাধান নেই? কিছু গাণিতিক সূত্র হতে পারে? আমার একটি ধারণা আছে যে এটি calculated_priceকিছু পরিবর্তনশীলগুলির সাথে অনুপাত Xএবং যখন মুদ্রা পরিবর্তন হয়, আমরা কেবলমাত্র সেই পরিবর্তনশীলটিকেই আপডেট করি X, না calculated_price, তাই আমাদের এমনকি কিছু কিছু (সারি) আপডেট করার দরকার নেই ... সম্ভবত কিছু গণিতবিদ এটি সমাধান করতে পারেন এটার মত?

উত্তর:


4

এখানে আলাদা পদ্ধতি রয়েছে যার জন্য পুনরায় পুনর্নির্মাণ calculated_priceকরা কেবলমাত্র একটি অপ্টিমাইজেশন, কঠোরভাবে প্রয়োজনীয় হওয়ার বিপরীতে।

মনে করুন যে currenciesটেবিলগুলিতে, আপনি অন্য কলাম যুক্ত করেছেন, এটি সর্বশেষ আপডেট হওয়ার last_rateসময় বিনিময় হার ধারণ করে, এটি calculated_priceকখন ঘটেছিল তা বিবেচনা করে না।

দামের পয়েন্টের সাথে দ্রুত পণ্যের সেটগুলি পুনরুদ্ধার করতে, বলুন, 50 ডলার এবং 100 ডলার যা পছন্দসই ফলাফলগুলি অন্তর্ভুক্ত করে, আপনি এরকম কিছু করতে পারেন:

  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))

যেখানে :last_rateসর্বশেষ আপডেটের সময় EUR / USD বিনিময় হার রয়েছে contains ধারণাটি হ'ল প্রতিটি মুদ্রার সর্বাধিক প্রকরণকে বিবেচনায় রাখতে অন্তর বাড়ানো। ব্যবধানের উভয় প্রান্তের বৃদ্ধির কারণগুলি হারের আপডেটের মধ্যে ধ্রুবক, তাই এগুলি প্রাক-গণনা করা যায়।

যেহেতু হারগুলি খুব অল্প সময়ের মধ্যে সামান্য পরিবর্তিত হয়েছে, উপরের ক্যোয়ারীটি চূড়ান্ত ফলাফলের একটি ঘনিষ্ঠভাবে কাছাকাছি দিতে পারে to চূড়ান্ত ফলাফল পেতে, আসুন শেষ আপডেটের পর থেকে হারের পরিবর্তনের কারণে দামগুলি সীমা ছাড়িয়ে গেছে এমন পণ্যগুলিকে ফিল্টার করুন 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 < 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;

:current_rateব্যবহারকারীর পয়সা বেছে নেওয়ার জন্য EUR এর সাথে আরও আপ-টু-ডেট রেট কোথায় ।

দক্ষতা বাস্তবতার থেকে আসে যে হারগুলির পরিসীমা ছোট বলে মনে করা হয়, মানগুলি একত্রে কাছাকাছি থাকা।


2

এটি একটি বস্তুগত দেখার জন্য একটি কাজের মতো শোনাচ্ছে। পোস্টগ্র্রেএসকিউএল স্পষ্টভাবে তাদের সমর্থন করে না, আপনি সাধারণ টেবিলগুলিতে ফাংশন এবং ট্রিগার ব্যবহার করে বস্তুগত দৃষ্টিভঙ্গি তৈরি এবং বজায় রাখতে পারেন।

আমি করতাম:

  • products_summaryআপনার বর্তমান productsসারণির স্কিমা সহ একটি নতুন টেবিল তৈরি করুন ;
  • ALTER TABLE products DROP COLUMN calculated_priceভিতরে calculated_priceকলাম থেকে মুক্তি পেতেproducts
  • একটি দৃশ্য যে আউটপুট তোমার জন্য চান উত্পাদন করে লিখুন products_summaryদ্বারা SELECTথেকে ing productsএবং JOINউপর ing currencies। আমি এটি কল করব products_summary_dynamicকিন্তু নামকরণটি আপনার উপর নির্ভর করে। আপনি চাইলে দেখার পরিবর্তে কোনও ফাংশন ব্যবহার করতে পারেন।
  • পর্যায়ক্রমে রূপায়িত দৃশ্য টেবিল রিফ্রেশ products_summaryথেকে products_summary_dynamicসঙ্গে BEGIN; TRUNCATE products_summary; INSERT INTO products_summary SELECT * FROM products_summary_dynamic; COMMIT;
  • একটি AFTER INSERT OR UPDATE OR DELETE ON productsট্রিগার তৈরি করুন যা products_summaryটেবিলটি বজায় রাখার জন্য একটি ট্রিগার প্রক্রিয়া চালায় , মুছে ফেলা হলে সারিগুলি মুছে ফেলা হবে products, যুক্ত হওয়ার পরে এগুলি যুক্ত করুন products( ভিউ SELECTথেকে আইং দিয়ে products_summary_dynamic) এবং পণ্যের বিবরণ পরিবর্তন হওয়ার সাথে সাথে সেগুলি আপডেট করুন।

এই পদ্ধতির সংক্ষিপ্তসার সারণি আপডেট করে এমন লেনদেনের products_summaryসময় একচেটিয়া লক নেওয়া হবে TRUNCATE ..; INSERT ...;। যদি এটি আপনার অ্যাপ্লিকেশনটিতে স্টলগুলির কারণ হয়ে থাকে কারণ এটি এত দীর্ঘ সময় নেয়, আপনি পরিবর্তে products_summaryটেবিলের দুটি সংস্করণ রাখতে পারেন । ব্যবহারযোগ্য নয় এমন একটি আপডেট করুন, তারপরে কোনও লেনদেনেALTER TABLE products_summary RENAME TO products_summary_old; ALTER TABLE products_summary_new RENAME TO products_summary;


একটি বিকল্প তবে খুব অদ্ভুত পদ্ধতির একটি এক্সপ্রেশন সূচক ব্যবহার করা হবে। কারণ এই পদ্ধতির সাথে মুদ্রা টেবিল আপডেট করার সময় সম্ভবত একটি অনিবার্যভাবে একটি লক প্রয়োজন DROP INDEXএবং CREATE INDEXআমি এটি প্রায়শই করতাম না - তবে এটি কিছু পরিস্থিতিতে উপযুক্ত হতে পারে।

ধারণাটি হ'ল কোনও IMMUTABLEফাংশনে আপনার মুদ্রা রূপান্তরটি গুটিয়ে রাখা । যেহেতু IMMUTABLEআপনি এটি ডাটাবেস ইঞ্জিনকে গ্যারান্টি দিচ্ছেন যে কোনও প্রদত্ত যুক্তিগুলির জন্য ফেরতের মান সর্বদা একই থাকবে এবং যদি ফেরতের মান পৃথক হয় তবে সমস্ত প্রকার পাগল জিনিসই নির্দ্বিধায়। ফাংশনটি কল করুন, বলুন to_euros(amount numeric, currency char(3)) returns numeric। আপনি চান এটি প্রয়োগ করুন; CASEমুদ্রা দ্বারা একটি বড় বিবৃতি, যা দেখার জন্য একটি টেবিল,। আপনি যদি সন্ধানের টেবিল ব্যবহার করেন তবে নীচের বর্ণিত ব্যতীত আপনার কখনই সারণী পরিবর্তন করা উচিত নয়

এর উপর একটি এক্সপ্রেশন সূচক তৈরি করুন products:

CREATE INDEX products_calculated_price_idx
ON products( to_euros(price,currency) );

আপনি এখন গণনা করা দাম অনুসারে পণ্যগুলির জন্য দ্রুত অনুসন্ধান করতে পারেন, যেমন:

SELECT *
FROM products
WHERE to_euros(price,currency) BETWEEN $1 and $2;

মুদ্রার টেবিলগুলি কীভাবে আপডেট করা যায় তা এখন সমস্যা। এখানে কৌশলটি হ'ল আপনি মুদ্রার টেবিলগুলি পরিবর্তন করতে পারেন , এটি করার জন্য আপনাকে কেবল সূচিটি ফেলে এবং পুনরায় তৈরি করতে হবে।

BEGIN;

-- An exclusive lock will be held from here until commit:
DROP INDEX products_calculated_price_idx;
DROP FUNCTION to_euros(amount numeric, currency char(3)) CASCADE;

-- It's probably better to use a big CASE statement here
-- rather than selecting from the `currencies` table as shown.
-- You could dynamically regenerate the function with PL/PgSQL
-- `EXECUTE` if you really wanted.
--
CREATE FUNCTION to_euros(amount numeric, currency char(3))
RETURNS numeric LANGUAGE sql AS $$
SELECT $1 / value FROM currencies WHERE id = $2;
$$ IMMUTABLE;

-- This may take some time and will run with the exclusive lock
-- held.
CREATE INDEX products_calculated_price_idx
ON products( to_euros(price,currency) );

COMMIT;

আমি উপরের ফাংশনটিকে কেবল ড্রপ এবং পুনরায় সংজ্ঞায়িত করছি যে আপনি যদি চাপ পরিবর্তনযোগ্য ফাংশনটিকে পুনরায় সংজ্ঞায়িত করেন তবে আপনাকে অবশ্যই ফাংশনটি ব্যবহার করে এমন সমস্ত কিছু ফেলে দিতে হবে drop একটি CASCADEড্রপ ব্যবহার করা এটি করার সর্বোত্তম উপায়।

আমি দৃ strongly়ভাবে সন্দেহ করি যে একটি বস্তুগত দৃষ্টিভঙ্গি আরও ভাল পদ্ধতির। এটি অবশ্যই নিরাপদ। আমি এটিকে বেশিরভাগ কিকের জন্য অন্তর্ভুক্ত করছি।


এই মুহুর্তে আমি এই সম্পর্কে ভাবছি - আমি কেন একেবারে আপডেট করব calculated_price? আমি কেবল initial_currency_value(ধীরে ধীরে মুদ্রার হার নেওয়া হয়, আজ বলা যাক) সংরক্ষণ করতে পারি এবং সর্বদা তার বিপরীতে গণনা করতে পারি! এবং ইউরোতে মূল্য প্রদর্শন করার সময় অবশ্যই আসল মুদ্রার হারের বিরুদ্ধে গণনা করুন। আমি কি সঠিক? বা এমন একটি সমস্যা আছে যা আমি দেখছি না?
তাইাই

1

আমি আমার নিজের ধারণা নিয়ে এসেছি। বলুন যদি এটি আসলে কাজ করে চলেছে, দয়া করে!

সমস্যাটি.

যখন পণ্যটি productsসারণীতে যুক্ত করা হচ্ছে তখন দামটি ডিফল্ট মুদ্রায় (EUR) রূপান্তরিত হয় এবং calculated_priceকলামে সঞ্চিত হয় ।

আমরা চাই যে ব্যবহারকারী যে কোনও মুদ্রার দামগুলি (ফিল্টার) অনুসন্ধান করতে পারেন। এটি ইনপুট মূল্যকে ডিফল্ট মুদ্রায় (EUR) রূপান্তর করে এবং calculated_priceকলামের সাথে তুলনা করে সম্পন্ন হয়েছে ।

আমাদের মুদ্রার হারগুলি আপডেট করতে হবে, যাতে ব্যবহারকারীরা তাজা মুদ্রার হার দ্বারা অনুসন্ধান করতে সক্ষম হবেন। তবে সমস্যাটি হ'ল কীভাবে calculated_priceদক্ষতার সাথে আপডেট করা যায় ।

সমাধান (আশা)।

কীভাবে calculated_priceদক্ষতার সাথে আপডেট করা যায় ।

করবেন না! :)

ধারণাটি হ'ল আমরা গতকালের মুদ্রার হারগুলি ( একই তারিখের সমস্ত ) কেবলমাত্রcalculated_price ব্যবহারের ক্ষেত্রেই গ্রহণ করি । ভালো লেগেছে ... সব সময় প্রবেশ করুন! প্রতিদিনের আপডেট নেই। দামগুলি তুলনা / ফিল্টার / অনুসন্ধান করার আগে কেবলমাত্র আমাদের প্রয়োজন আজকের মুদ্রার হারগুলি যেমন গতকালের মতো ছিল তাই নেওয়া।

সুতরাং, calculated_priceআমরা কেবলমাত্র নির্দিষ্ট তারিখের মুদ্রার হার ব্যবহার করব (আমরা বেছে নিয়েছি, গতকাল বলি)। আমাদের যা প্রয়োজন তা হ'ল আজকের দামটিকে গতকালের দামে রূপান্তর করা। অন্য কথায়, আজকের হারটি নিন এবং এটি গতকালের হারে রূপান্তর করুন:

cash_in_euros * ( rate_newest / rate_fixed )

এবং এটি মুদ্রার টেবিল:

CREATE TABLE "currencies" (
  "id" char(3) NOT NULL, -- currency code (EUR, USD, GBP, ...)
  "is_default" boolean NOT NULL DEFAULT 'f',

  -- Set once. If you update, update all database fields that depends on this.
  "rate_fixed" numeric NOT NULL, -- Currency rate against default currency
  "rate_fixed_updated" timestamp NOT NULL,

  -- Update as frequently as needed.
  "rate_newest" numeric NOT NULL, -- Currency rate against default currency
  "rate_newest_updated" timestamp NOT NULL,

  CONSTRAINT "currencies_id_pkey" PRIMARY KEY ("id")
);

এটি এমন কীভাবে 200 মিলিয়ন মার্কিন ডলার ব্যয় করা পণ্যটি কীভাবে যুক্ত করা যায় এবং কীভাবে calculated_priceগণনা করা হয়: ইউএসডি থেকে নতুন ইউরো হার এবং স্থির (পুরানো) হারে

INSERT INTO "products" (price, currency, calculated_price)
  SELECT
  200.0 AS price,
  'USD' AS currency,

  ((200.0 / rate_newest) * (rate_newest / rate_fixed)) AS calculated_price

    FROM "currencies" WHERE id = 'USD';

এটি ক্লায়েন্টের দিক থেকে প্রাক-গণনা করা যেতে পারে এবং এটিই আমি যা করতে যাচ্ছি - calculated_priceআমরা কোনও ক্যোয়ারী করার আগে ব্যবহারকারীর ইনপুট মূল্যটিকে সামঞ্জস্যপূর্ণ মান হিসাবে গণনা করুন , যাতে ভাল পুরানো ব্যবহার করা হবেSELECT * FROM products WHERE calculated_price > 100.0 AND calculated_price < 200.0;

উপসংহার।

এই ধারণাটি আমার কাছে কয়েক ঘন্টা আগে এসেছিল এবং বর্তমানে আমি আপনাকে এই সমাধান সম্পর্কে সঠিক কিনা তা পরীক্ষা করতে বলছি। আপনি কি মনে করেন? এটি কি কাজ করবে? নাকি আমার ভুল হয়েছে?

আমি আশা করি আপনি এই সব বুঝতে পারবেন। আমি নেটিভ ইংলিশ স্পিকার নই, দেরি হয়ে গেছে এবং আমি ক্লান্ত হয়ে পড়েছি। :)

হালনাগাদ

ঠিক আছে, দেখে মনে হচ্ছে এটি একটি সমস্যার সমাধান করে তবে অন্যটির পরিচয় দেয়। খুব খারাপ. :)


সমস্যাটি হ'ল rate_newest / rate_fixedমুদ্রা অনুযায়ী এটি পৃথক এবং এই সমাধানটি ব্যবহারকারী-সন্ধানের অর্থ অনুসন্ধানের জন্য একমাত্র বিবেচনা করে। আলাদা মুদ্রার যে কোনও দামই আপ টু ডেট রেটের সাথে তুলনা করা হবে না। আমি যে উত্তরটি জমা দিয়েছি তাতে কোনও একরকম সমস্যা হয়েছিল তবে আমি মনে করি এটি আপডেট করা সংস্করণে ঠিক করেছি।
ড্যানিয়েল ভ্যারিটি

এই পদ্ধতির সাথে আমি যে প্রধান সমস্যাটি দেখতে পাচ্ছি তা হ'ল এটি দামের উপর ডাটাবেস সূচকগুলির সুবিধা গ্রহণ করে না (ক্যালকুলেটেড_প্রাইজ ক্লজ দ্বারা অর্ডার)।
রোজেনফিল্ড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.