একটি যোগ করা সারণীতে একত্রিত মানের ক্রমবর্ধমান সংখ্যা পান


10

আমার একটি মাইএসকিউএল 5.7.22 ডাটাবেসে দুটি টেবিল রয়েছে: postsএবং reasons। প্রতিটি পোস্ট সারিতে অনেকগুলি কারণ সারি রয়েছে এবং এর সাথে সম্পর্কিত। প্রতিটি কারণের সাথে এর সাথে যুক্ত ওজন থাকে এবং প্রতিটি পোস্টের সাথে এটির সাথে মোট সামগ্রিক ওজন যুক্ত থাকে।

10 পয়েন্ট ওজনের প্রতিটি বৃদ্ধির জন্য (উদাহরণস্বরূপ 0, 10, 20, 30, ইত্যাদি) এর জন্য, আমি এমন একটি পোস্টের একটি কাউন্ট পেতে চাই যার মোট ওজন সেই বর্ধনের চেয়ে কম বা সমান have আমি ফলাফলগুলি এর মতো দেখতে কিছু প্রত্যাশা করব:

 weight | post_count
--------+------------
      0 | 0
     10 | 5
     20 | 12
     30 | 18
    ... | ...
    280 | 20918
    290 | 21102
    ... | ...
   1250 | 118005
   1260 | 118039
   1270 | 118040

মোট ওজন প্রায় খুব সাধারণভাবে বিতরণ করা হয়, কয়েকটি খুব কম মান এবং কয়েকটি খুব উচ্চ মানের (সর্বাধিক বর্তমানে 1277), তবে মাঝের সংখ্যাগরিষ্ঠ। এখানে কেবলমাত্র 120,000 সারি রয়েছে postsএবং প্রায় 120 টি সারি রয়েছে reasons। প্রতিটি পোস্টের গড়ে 5 বা 6 টি কারণ রয়েছে।

টেবিলগুলির প্রাসঙ্গিক অংশগুলি এর মতো দেখাচ্ছে:

CREATE TABLE `posts` (
  id BIGINT PRIMARY KEY
);

CREATE TABLE `reasons` (
  id BIGINT PRIMARY KEY,
  weight INT(11) NOT NULL
);

CREATE TABLE `posts_reasons` (
  post_id BIGINT NOT NULL,
  reason_id BIGINT NOT NULL,
  CONSTRAINT fk_posts_reasons_posts (post_id) REFERENCES posts(id),
  CONSTRAINT fk_posts_reasons_reasons (reason_id) REFERENCES reasons(id)
);

এখনও অবধি, আমি পোস্ট আইডি এবং মোট ওজনকে একটি ভিউতে ফেলে দেওয়ার চেষ্টা করেছি , তারপরে একটি সংখ্যক গণনা পাওয়ার জন্য নিজের মধ্যে সেই ভিউটিতে যোগ দিয়েছি :

CREATE VIEW `post_weights` AS (
    SELECT 
        posts.id,
        SUM(reasons.weight) AS reason_weight
    FROM posts
    INNER JOIN posts_reasons ON posts.id = posts_reasons.post_id
    INNER JOIN reasons ON posts_reasons.reason_id = reasons.id
    GROUP BY posts.id
);

SELECT
    FLOOR(p1.reason_weight / 10) AS weight,
    COUNT(DISTINCT p2.id) AS cumulative
FROM post_weights AS p1
INNER JOIN post_weights AS p2 ON FLOOR(p2.reason_weight / 10) <= FLOOR(p1.reason_weight / 10)
GROUP BY FLOOR(p1.reason_weight / 10)
ORDER BY FLOOR(p1.reason_weight / 10) ASC;

এটি, তবে, অসাধারণভাবে ধীর - আমি এটি 15 মিনিটের জন্য শেষ না করে চালিয়ে দেই, যা আমি উত্পাদন করতে পারি না।

এটি করার আরও কার্যকর উপায় আছে?

আপনি যদি পুরো ডেটাसेट পরীক্ষা করতে আগ্রহী হন তবে এটি এখানে ডাউনলোডযোগ্য । ফাইলটি 60MB এর কাছাকাছি, এটি 250MB এর কাছাকাছি প্রসারিত হয়। পর্যায়ক্রমে, এখানে গিটহাবের টুকরোতে 12,000 সারি রয়েছে ।

উত্তর:


8

JOIN অবস্থায় ফাংশন বা এক্সপ্রেশন ব্যবহার করা সাধারণত একটি খারাপ ধারণা, আমি সাধারণত বলি কারণ কিছু অপটিমাইজাররা এটি মোটামুটিভাবে পরিচালনা করতে পারে এবং যেভাবেই সূচকগুলি ব্যবহার করতে পারে। আমি ওজনের জন্য একটি টেবিল তৈরি করার পরামর্শ দেব। কিছুটা এইরকম:

CREATE TABLE weights
( weight int not null primary key 
);

INSERT INTO weights (weight) VALUES (0),(10),(20),...(1270);

আপনার সূচী আছে তা নিশ্চিত করুন posts_reasons:

CREATE UNIQUE INDEX ... ON posts_reasons (reason_id, post_id);

এর মতো একটি কোয়েরি:

SELECT w.weight
     , COUNT(1) as post_count
FROM weights w
JOIN ( SELECT pr.post_id, SUM(r.weight) as sum_weight     
       FROM reasons r
       JOIN posts_reasons pr
             ON r.id = pr.reason_id
       GROUP BY pr.post_id
     ) as x
    ON w.weight > x.sum_weight
GROUP BY w.weight;

বাড়িতে আমার মেশিনটি সম্ভবত 5-6 বছর বয়সী, এটিতে একটি ইন্টেল (আর) কোর (টিএম) i5-3470 সিপিইউ @ 3.20GHz এবং 8 জিবি র‌্যাম রয়েছে।

uname-a লিনাক্স ডাস্টবাইট 4.16.6-302.fc28.x86_64 # 1 এসএমপি বুধ 2 মে 00:07:06 ইউটিসি 2018 x86_64 x86_64 x86_64 জিএনইউ / লিনাক্স

আমি এর বিরুদ্ধে পরীক্ষা করেছি:

https://drive.google.com/open?id=1q3HZXW_qIZ01gU-Krms7qMJW3GCsOUP5

MariaDB [test3]> select @@version;
+-----------------+
| @@version       |
+-----------------+
| 10.2.14-MariaDB |
+-----------------+
1 row in set (0.00 sec)


SELECT w.weight
     , COUNT(1) as post_count
FROM weights w
JOIN ( SELECT pr.post_id, SUM(r.weight) as sum_weight     
       FROM reasons r
       JOIN posts_reasons pr
             ON r.id = pr.reason_id
       GROUP BY pr.post_id
     ) as x
    ON w.weight > x.sum_weight
GROUP BY w.weight;

+--------+------------+
| weight | post_count |
+--------+------------+
|      0 |          1 |
|     10 |       2591 |
|     20 |       4264 |
|     30 |       4386 |
|     40 |       5415 |
|     50 |       7499 |
[...]   
|   1270 |     119283 |
|   1320 |     119286 |
|   1330 |     119286 |
[...]
|   2590 |     119286 |
+--------+------------+
256 rows in set (9.89 sec)

যদি পারফরম্যান্স সমালোচনামূলক হয় এবং অন্য কিছুই আপনাকে সহায়তা না করে তবে এর জন্য একটি সংক্ষিপ্তসার সারণি তৈরি করতে পারে:

SELECT pr.post_id, SUM(r.weight) as sum_weight     
FROM reasons r
JOIN posts_reasons pr
    ON r.id = pr.reason_id
GROUP BY pr.post_id

আপনি ট্রিগারগুলির মাধ্যমে এই টেবিলটি বজায় রাখতে পারেন

যেহেতু ওজনের প্রতিটি ওজনের জন্য একটি নির্দিষ্ট পরিমাণ কাজ করা দরকার, তাই এই টেবিলটি সীমাবদ্ধ করা সুবিধাজনক হতে পারে।

    ON w.weight > x.sum_weight 
WHERE w.weight <= (select MAX(sum_weights) 
                   from (SELECT SUM(weight) as sum_weights 
                   FROM reasons r        
                   JOIN posts_reasons pr
                       ON r.id = pr.reason_id 
                   GROUP BY pr.post_id) a
                  ) 
GROUP BY w.weight

যেহেতু আমার ওজন টেবিলে আমার প্রচুর অনিয়মিত সারি রয়েছে (সর্বাধিক 2590), উপরের সীমাবদ্ধতার ফলে নির্বাহের সময়টি 9 থেকে 4 সেকেন্ডে কেটে যায়।


স্পষ্টতা: এটি দেখে মনে হচ্ছে এটি ওজনের চেয়ে কম ওজনের কারণে কারণগুলি গণনা করছেw.weight - এটি কি ঠিক? আমি lte এর মোট ওজন (তাদের সম্পর্কিত কারণ সারিগুলির ওজনের যোগফল) সহ পোস্টগুলি গণনা করতে চাই w.weight
আর্টঅফকোড

ওহ দুঃখিত. আমি কোয়েরিটি আবার
লিখব

এটি আমার বাকি পথটি পেয়েছে, তবে, ধন্যবাদ! post_weightsআমি ইতিমধ্যে পরিবর্তে বিদ্যমান ভিউ থেকে নির্বাচন করা প্রয়োজন reasons
আর্টঅফকোড

@ আর্টঅফকোড, আমি কি এটি সংশোধিত প্রশ্নের জন্য সঠিক পেয়েছি? বিটিডাব্লু, একটি দুর্দান্ত প্রশ্নের জন্য ধন্যবাদ। পরিষ্কার, সংক্ষিপ্ত এবং প্রচুর নমুনা ডেটা সহ। ব্র্যাভো
লেনার্ট

7

মাইএসকিউএলে, ভেরিয়েবলগুলি কলামগুলিতে কলামগুলিতে মানগুলি থেকে গণনা করতে এবং নতুন, গণনা করা কলামগুলির জন্য প্রকাশে ব্যবহার করা যেতে পারে eries এই ক্ষেত্রে, একটি দক্ষ ক্যোয়ারিতে একটি পরিবর্তনশীল ফলাফল ব্যবহার:

SELECT
  weight,
  @cumulative := @cumulative + post_count AS post_count
FROM
  (SELECT @cumulative := 0) AS x,
  (
    SELECT
      FLOOR(reason_weight / 10) * 10 AS weight,
      COUNT(*)                       AS post_count
    FROM
      (
        SELECT 
          p.id,
          SUM(r.weight) AS reason_weight
        FROM
          posts AS p
          INNER JOIN posts_reasons AS pr ON p.id = pr.post_id
          INNER JOIN reasons AS r ON pr.reason_id = r.id
        GROUP BY
          p.id
      ) AS d
    GROUP BY
      FLOOR(reason_weight / 10)
    ORDER BY
      FLOOR(reason_weight / 10) ASC
  ) AS derived
;

dউদ্ভূত টেবিল আসলে আপনার হয় post_weightsদৃশ্য। অতএব, আপনি যদি ভিউটি রাখার পরিকল্পনা করে থাকেন তবে আপনি এটি উত্পন্ন টেবিলের পরিবর্তে ব্যবহার করতে পারেন:

SELECT
  weight,
  @cumulative := @cumulative + post_count AS post_count
FROM
  (SELECT @cumulative := 0),
  (
    SELECT
      FLOOR(reason_weight / 10) * 10 AS weight,
      COUNT(*)                       AS post_count
    FROM
      post_weights
    GROUP BY
      FLOOR(reason_weight / 10)
    ORDER BY
      FLOOR(reason_weight / 10) ASC
  ) AS derived
;

এই সমাধানের একটি ডেমো যা আপনার সেটআপের হ্রাস সংস্করণটির একটি সংক্ষিপ্ত সংস্করণ ব্যবহার করে এটি এসকিউএল ফিডল-এর সাথে খুঁজে পাওয়া যায় এবং প্লে করা যায় ।


আমি আপনার ডেটা পুরো ডেটা সেট দিয়ে চেষ্টা করেছি। আমি নিশ্চিত না কেন (ক্যোয়ারীটি আমার কাছে ঠিক দেখাচ্ছে) তবে মারিয়াডিবি ERROR 1055 (42000): 'd.reason_weight' isn't in GROUP BYযদি অভিযোগ ONLY_FULL_GROUP_BYকরেন যে @@ এসকিউএল_মোডে রয়েছে কিনা। এটি অক্ষম করে আমি লক্ষ্য করেছি যে আপনার ক্যোয়ারী আমার প্রথমবারের চেয়ে ধীর (11 ডলার) is একবার ডেটা ক্যাশে হয়ে গেলে এটি দ্রুত হয় (~ 1 সেকেন্ড)। আমার ক্যোয়ারী প্রতিবার প্রায় 4 সেকেন্ডে চলে।
লেনার্ট

1
@ লেনার্ট: কারণ এটি আসল কোয়েরি নয়। আমি এটিকে ফিডে সংশোধন করেছিলাম তবে উত্তরটি আপডেট করতে ভুলে গেছি। এখনই এটি আপডেট করা, মাথা আপ জন্য ধন্যবাদ।
অ্যান্ড্রি এম

@ লেনার্ট: পারফরম্যান্সের ক্ষেত্রে, এই ধরণের ক্যোয়ারী সম্পর্কে আমার একটি ভুল ধারণা থাকতে পারে। আমি ভেবেছিলাম এটি দক্ষতার সাথে কাজ করা উচিত কারণ টেবিলের এক পাসে গণনাগুলি সম্পূর্ণ হবে। সম্ভবত এটি উদ্ভূত টেবিলগুলির ক্ষেত্রে অগত্যা নয়, বিশেষত যারা সমষ্টি ব্যবহার করে। আমি ভয় করি যদিও আমার কাছে সঠিক মাইএসকিউএল ইনস্টলেশন বা গভীরতর বিশ্লেষণের জন্য পর্যাপ্ত দক্ষতা নেই।
অ্যান্ড্রি এম

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