তারিখের ব্যবধানে গড় পরিমাণ / গণনা / গড় ঘূর্ণায়মান


20

18 মাসেরও বেশি সময় ধরে 1000,000 সত্তা লেনদেনের একটি ডাটাবেসে, আমি 30 দিনের মেয়াদে entity_idতাদের লেনদেনের পরিমাণের একটি এসএমএম এবং সেই 30 দিনের সময়কালে তাদের লেনদেনের COUNT দ্বারা প্রতিটি সম্ভাব্য 30-দিনের পিরিয়ডকে গোষ্ঠীবদ্ধ করতে একটি কোয়েরি চালাতে চাই এবং এমনভাবে ডেটা ফেরত দিন যাতে আমি তারপরে জিজ্ঞাসা করতে পারি। অনেক পরীক্ষার পরে, এই কোডটি আমি যা চাই তাতে অনেকটাই সফল করে তোলে:

SELECT id, trans_ref_no, amount, trans_date, entity_id,
    SUM(amount) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_total,
    COUNT(id)   OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_count
  FROM transactiondb;

এবং আমি বৃহত্তর ক্যোয়ারী কাঠামোগত কিছুতে ব্যবহার করব:

SELECT * FROM (
  SELECT id, trans_ref_no, amount, trans_date, entity_id,
      SUM(amount) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_total,
      COUNT(id)   OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_count
    FROM transactiondb ) q
WHERE trans_count >= 4
AND trans_total >= 50000;

এই ক্যোয়ারীটি যে বিষয়টিকে অন্তর্ভুক্ত করে না তা হ'ল লেনদেনের গণনা একাধিক মাস পর্যন্ত বিস্তৃত হয় তবে এখনও একে অপরের 30 দিনের মধ্যে থাকে। পোস্টগ্রিসের সাথে কি এই জাতীয় কোয়েরি সম্ভব? যদি তা হয় তবে আমি কোনও ইনপুটকে স্বাগত জানাই। অন্যান্য বিষয়গুলির মধ্যে অনেকগুলি " চলমান " সমষ্টি সম্পর্কে আলোচনা করে, ঘূর্ণায়মান নয় ।

হালনাগাদ

CREATE TABLEস্ক্রিপ্ট:

CREATE TABLE transactiondb (
    id integer NOT NULL,
    trans_ref_no character varying(255),
    amount numeric(18,2),
    trans_date date,
    entity_id integer
);

নমুনা তথ্য এখানে পাওয়া যাবে । আমি পোস্টগ্রিসকিউএল 9.1.16 চালাচ্ছি।

আদর্শ আউটপুট অন্তর্ভুক্ত SUM(amount)এবং COUNT()30 দিনের-রোলিং সময়ের মধ্যে সমস্ত লেনদেনের অন্তর্ভুক্ত থাকবে । এই চিত্রটি দেখুন, উদাহরণস্বরূপ:

সারিগুলির উদাহরণ যা আদর্শভাবে একটি "সেট" এ অন্তর্ভুক্ত হবে তবে তা নয় কারণ আমার সেটটি মাসিক দ্বারা স্থির।

সবুজ তারিখ হাইলাইটিং আমার ক্যোয়ারী দ্বারা কী অন্তর্ভুক্ত হচ্ছে তা নির্দেশ করে। হলুদ সারি হাইলাইট করা রেকর্ডগুলি নির্দেশ করে যা আমি সেটটির অংশ হতে চাই।

পূর্ববর্তী পড়া:


1
দ্বারা every possible 30-day period by entity_idআপনি কি বোঝাতে চেয়েছেন সময়ের শুরু করতে পারেন কোনো দিন (অ-লিপ) বছরের তাই 365 সম্ভব সময়সীমার? বা আপনি কি কোনও ব্যক্তির জন্য পৃথকভাবে কোনও সময়ের শুরু হিসাবে সত্যিকারের লেনদেনের দিনগুলি বিবেচনা করতে চান entity_id ? যে কোনও উপায়ে, দয়া করে আপনার টেবিল সংজ্ঞা, পোস্টগ্রিস সংস্করণ, কিছু নমুনার ডেটা এবং নমুনার প্রত্যাশিত ফলাফল সরবরাহ করুন।
এরউইন ব্র্যান্ডসেটেটার

তত্ত্বগতভাবে, আমি যে কোনও দিন বোঝাতে চাইছি, তবে বাস্তবে কোনও দিন লেনদেন নেই এমন দিনগুলি বিবেচনা করার দরকার নেই। আমি নমুনা ডেটা এবং টেবিল সংজ্ঞা পোস্ট করেছি।
tufelkinder

সুতরাং আপনি প্রতিটি আসল লেনদেন থেকেentity_id 30 দিনের উইন্ডোতে একই সারিগুলি সংগ্রহ করতে চান । একই জন্য একাধিক লেনদেন হতে পারে বা এটি সংমিশ্রণটি অনন্য সংজ্ঞায়িত হয়েছে? আপনার টেবিলের সংজ্ঞায় কোনও বা পিকে বাধা নেই, তবে মনে হচ্ছে যে প্রতিবন্ধকতাগুলি অনুপস্থিত ...(trans_date, entity_id)UNIQUE
এরউইন ব্র্যান্ডসেটেটার

একমাত্র বাধাটি idপ্রাথমিক কীতে রয়েছে। প্রতিদিন সত্তা প্রতি একাধিক লেনদেন হতে পারে।
tufelkinder

ডেটা বিতরণ সম্পর্কে: বেশিরভাগ দিন কি প্রবেশ (প্রতি সত্তা_আইডি) রয়েছে?
এরউইন ব্র্যান্ডসেটেটার

উত্তর:


26

আপনার কাছে ক্যোয়ারী রয়েছে

আপনি একটি WINDOWক্লজ ব্যবহার করে আপনার ক্যোয়ারীটিকে সহজতর করতে পারেন , তবে এটি কেবল বাক্য গঠনটি সংক্ষিপ্ত করছে, কোয়েরি প্ল্যানটি পরিবর্তন করছে না।

SELECT id, trans_ref_no, amount, trans_date, entity_id
     , SUM(amount) OVER w AS trans_total
     , COUNT(*)    OVER w AS trans_count
FROM   transactiondb
WINDOW w AS (PARTITION BY entity_id, date_trunc('month',trans_date)
             ORDER BY trans_date
             ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING);
  • এছাড়াও সামান্য দ্রুত ব্যবহার count(*), যেহেতু idঅবশ্যই সংজ্ঞায়িত করা হয় NOT NULL?
  • ORDER BY entity_idআপনার ইতিমধ্যে আপনার প্রয়োজন নেইPARTITION BY entity_id

আপনি আরও সরল করতে পারেন, যদিও: উইন্ডো সংজ্ঞাটি
মোটেও যুক্ত করবেন না ORDER BY, এটি আপনার প্রশ্নের সাথে প্রাসঙ্গিক নয়। তাহলে আপনার পছন্দসই উইন্ডো ফ্রেমটি সংজ্ঞায়িত করার দরকার নেই, হয়:

SELECT id, trans_ref_no, amount, trans_date, entity_id
     , SUM(amount) OVER w AS trans_total
     , COUNT(*)    OVER w AS trans_count
FROM   transactiondb
WINDOW w AS (PARTITION BY entity_id, date_trunc('month',trans_date);

সহজ, দ্রুত, তবে স্থির মাস সহ আপনার কাছে যা আছে তার কেবলমাত্র একটি আরও ভাল সংস্করণ ।

আপনি যে প্রশ্নটি চাইতে পারেন

... পরিষ্কারভাবে সংজ্ঞায়িত করা হয়নি, তাই আমি এই অনুমানগুলি তৈরি করব:

যেকোনও প্রথম এবং শেষ লেনদেনের মধ্যে প্রতি 30-দিনের সময়ের জন্য লেনদেন এবং পরিমাণ গণনা করুন entity_id। কোনও ক্রিয়াকলাপ ছাড়াই অগ্রণী এবং অনুসরণের সময়সীমা বাদ দিন, তবে এই বাহ্যিক সীমার মধ্যে সমস্ত সম্ভাব্য 30-দিনের সময়কাল অন্তর্ভুক্ত করুন।

SELECT entity_id, trans_date
     , COALESCE(sum(daily_amount) OVER w, 0) AS trans_total
     , COALESCE(sum(daily_count)  OVER w, 0) AS trans_count
FROM  (
   SELECT entity_id
        , generate_series (min(trans_date)::timestamp
                         , GREATEST(min(trans_date), max(trans_date) - 29)::timestamp
                         , interval '1 day')::date AS trans_date
   FROM   transactiondb 
   GROUP  BY 1
   ) x
LEFT JOIN (
   SELECT entity_id, trans_date
        , sum(amount) AS daily_amount, count(*) AS daily_count
   FROM   transactiondb
   GROUP  BY 1, 2
   ) t USING (entity_id, trans_date)
WINDOW w AS (PARTITION BY entity_id ORDER BY trans_date
             ROWS BETWEEN CURRENT ROW AND 29 FOLLOWING);

এটি entity_idআপনার সমষ্টিগুলির সাথে এবং পিরিয়ডের trans_dateপ্রথম দিন (সহ) এর সাথে প্রত্যেকের জন্য সমস্ত 30 দিনের পিরিয়ড তালিকা করে। প্রতিটি স্বতন্ত্র সারির মান পাওয়ার জন্য বেস টেবিলে আরও একবার যোগ দিন ...

মূল সমস্যাটি এখানে আলোচিত হিসাবে একই:

উইন্ডোটির ফ্রেম সংজ্ঞাটি বর্তমান সারির মানগুলির উপর নির্ভর করতে পারে না।

বরং ইনপুট generate_series()দিয়ে কল timestampকরুন:

আপনি যে ক্যোয়ারীটি চান তা আসলে

প্রশ্ন আপডেট এবং আলোচনার পরে: প্রতিটি আসল লেনদেনের সময় থেকে 30 দিনের উইন্ডোতে
একই সারিগুলি সংগ্রহ করুন entity_id

যেহেতু আপনার ডেটা অল্প পরিমাণে বিতরণ করা হয়েছে, তাই পরিসীমা শর্তের সাথে একটি স্ব-যোগদান চালানোর জন্য এটি আরও দক্ষ হওয়া উচিত , পোস্টগ্রিস 9.1 এর পরে যে কোনও LATERALযোগ নেই, এখনও:

SELECT t0.id, t0.amount, t0.trans_date, t0.entity_id
     , sum(t1.amount) AS trans_total, count(*) AS trans_count
FROM   transactiondb t0
JOIN   transactiondb t1 USING (entity_id)
WHERE  t1.trans_date >= t0.trans_date
AND    t1.trans_date <  t0.trans_date + 30  -- exclude upper bound
-- AND    t0.entity_id = 114284  -- or pick a single entity ...
GROUP  BY t0.id  -- is PK!
ORDER  BY t0.trans_date, t0.id

এসকিউএল ফিডল।

একটি ঘূর্ণায়মান উইন্ডো বেশিরভাগ দিন ডেটার সাথে কেবল (পারফরম্যান্সের প্রতি শ্রদ্ধার সাথে) বোঝাতে পারে।

এই আছে না উপর সমষ্টিগত সদৃশ (trans_date, entity_id)দিন প্রতি, কিন্তু একই দিনের সব সারি সবসময় 30 দিনের উইন্ডোতে অন্তর্ভুক্ত করা হয়।

একটি বড় টেবিলের জন্য, এর মতো একটি আচ্ছাদন সূচকটি কিছুটা সহায়তা করতে পারে:

CREATE INDEX transactiondb_foo_idx
ON transactiondb (entity_id, trans_date, amount);

amountআপনি কেবলমাত্র সূচি-স্ক্যানগুলি থেকে বের হয়ে গেলে কেবলমাত্র শেষ কলামটি কার্যকর। অন্যথায় এটি ছেড়ে দিন।

আপনি যে কোনওভাবে পুরো টেবিলটি নির্বাচন করার সময় এটি ব্যবহার করা হবে না। এটি একটি ছোট উপসেটের জন্য প্রশ্নের সহায়তা করবে।


এটি সত্যিই দুর্দান্ত দেখাচ্ছে, এখনই এটি ডেটাতে পরীক্ষা করে দেখছে এবং আপনার ক্যোয়ারীটি আসলে যা করছে তা সমস্ত কিছু বোঝার চেষ্টা করছে ...
tufelkinder

@ টুফেলকিন্ডার: আপডেট হওয়া প্রশ্নের জন্য একটি সমাধান যুক্ত করেছে।
এরউইন ব্র্যান্ডসটেটার

এখনই এটি পর্যালোচনা। আমি আগ্রহী যে এটি এসকিউএল column "t0.amount" must appear in the GROUP BY clause...
ফিডেলে চলেছে

@ টুফেলকিন্ডার: আমি পরীক্ষার কেসটি প্রায় 100 টি করে কেটে ফেলেছি। স্ক্যালফিল্ড পরীক্ষার ডেটার আকার সীমাবদ্ধ করে। জেক (লেখক) কয়েক মাস আগে সীমাবদ্ধতার সীমাটি হ্রাস করেছিল তাই সাইটটি খুব সহজেই স্থবির হয়ে পড়ে।
এরউইন ব্র্যান্ডসটেটার

1
বিলম্বের জন্য দুঃখিত, এটি সম্পূর্ণ ডাটাবেসে পরীক্ষা করার প্রয়োজন। আপনার উত্তরটি বরাবরের মতো দুর্দান্তভাবে গভীর এবং শিক্ষামূলক ছিল। ধন্যবাদ!
tufelkinder
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.