মাইএসকিউএল দিয়ে মিডিয়ান গণনা করার সহজ উপায়


207

মাইএসকিউএল দিয়ে মিডিয়্যান গণনা করার সবচেয়ে সহজ (এবং আশা করি খুব ধীর নয়) উপায় কী? আমি অর্থটি সন্ধান করার AVG(x)জন্য ব্যবহার করেছি , তবে মধ্যকটি গণনার সহজ উপায় খুঁজে পেতে আমার খুব কষ্ট হচ্ছে। আপাতত, আমি সমস্ত সারি পিএইচপি-তে ফিরিয়ে দিচ্ছি, বাছাই করে, এবং তারপরে মাঝের সারিটি বাছাই করছি, তবে অবশ্যই এটি একক মাইএসকিউএল কোয়েরিতে করার কিছু সহজ উপায় থাকতে হবে।

উদাহরণ ডেটা:

id | val
--------
 1    4
 2    7
 3    2
 4    2
 5    9
 6    8
 7    3

উপর বাছাই valদেয় 2 2 3 4 7 8 9, তাই মধ্যমা হওয়া উচিত 4, বনাম SELECT AVG(val)যা == 5


71
মাইএসকিউএল এর একটি মিডিয়ান গণনা করার জন্য কোন ফাংশন নেই এই বিষয়টি শুনে আমি কি কেবলই বমি বোধ করি? হাস্যকর।
মনিকা হেডনেক 26'16

3
সংস্করণ 10.3-এর একটি হিসাবে মারিয়াডিবি-র একটি রয়েছে, দেখুন মারিয়াডবি.com
কেবি /

উত্তর:


224

মারিয়াডিবি / মাইএসকিউএল এ:

SELECT AVG(dd.val) as median_val
FROM (
SELECT d.val, @rownum:=@rownum+1 as `row_number`, @total_rows:=@rownum
  FROM data d, (SELECT @rownum:=0) r
  WHERE d.val is NOT NULL
  -- put some where clause here
  ORDER BY d.val
) as dd
WHERE dd.row_number IN ( FLOOR((@total_rows+1)/2), FLOOR((@total_rows+2)/2) );

স্টিভ কোহেন উল্লেখ করেছেন, প্রথম পাসের পরে, @ ডাউনামে মোট সারি সংখ্যা থাকবে। এটি মিডিয়ান নির্ধারণ করতে ব্যবহার করা যেতে পারে, সুতরাং দ্বিতীয় পাস বা যোগদানের দরকার নেই।

এছাড়াও AVG(dd.val)এবং dd.row_number IN(...)রেকর্ডের একটি এমনকি সংখ্যা আছে যখন সঠিকভাবে একটি মিডিয়ান উত্পাদন করতে ব্যবহৃত হয়। রিজনিং:

SELECT FLOOR((3+1)/2),FLOOR((3+2)/2); -- when total_rows is 3, avg rows 2 and 2
SELECT FLOOR((4+1)/2),FLOOR((4+2)/2); -- when total_rows is 4, avg rows 2 and 3

শেষ অবধি , মারিয়াডবি 10.3.3+ এ একটি মিডিয়া ফাংশন রয়েছে


4
গ্রুপ মান দেখানোর জন্য এটি কোনও উপায়? পছন্দ: সেই জায়গার জন্য স্থান / মিডিয়ান ... নির্বাচন করা স্থানের মতো, টেবিল থেকে মধ্যযুগীয় মূল্য ... কোনও উপায়ে? ধন্যবাদ
saulob

2
@rowNum এর ফাঁসির শেষে 'মোট গণনা' থাকবে। সুতরাং আপনি এটি ব্যবহার করতে পারেন যদি আপনি আবার 'সমস্ত গণনা' না করাতে চান তবে (যা আমার মামলা ছিল কারণ আমার প্রশ্নটি এত সহজ ছিল না)
আহমেদ-আনাস

একটি বিবৃতি থাকার যুক্তি: (মেঝে ((মোট_আরো + 1) / 2), তল ((মোট_আরো + 2) / 2)) মাঝারিটির জন্য প্রয়োজনীয় সারিগুলি গণনা করুন দুর্দান্ত! আপনি কীভাবে এটি ভেবেছিলেন তা নিশ্চিত নয় তবে এটি দুর্দান্ত। যে অংশটি আমি অনুসরণ করি না তা হ'ল (SELECT @rownum: = 0) r - এটি কোন উদ্দেশ্যে কাজ করে?
শনিমিস্টার

প্রথমটিতে WHERE 1এমন পরিবর্তন করুন WHERE d.val IS NOT NULLযাতে এটি NULLএই পদ্ধতির সাথে স্থানীয়ভাবে সারিবদ্ধ রাখার জন্য সারিগুলি বাদ দেয়AVG
চিলনিট

1
আমার মানটি একটি দ্বি-টেবিলের যোগ থেকে এসেছে, সুতরাং যোগদানের পরে সারি ক্রমটি সঠিক ছিল তা নিশ্চিত করতে আমাকে আরও একটি সাবকিউরি যুক্ত করতে হয়েছিল! গঠনটি ছিল সাজানোselect avg(value) from (select value, row_number from (select a - b as value from a_table join b_table order by value))
ড্যানিয়েল বাকমাস্টার

62

আমি মন্তব্যগুলিতে অনলাইনে আরও একটি উত্তর পেয়েছি :

প্রায় কোনও এসকিউএল এর মিডিয়ানদের জন্য:

SELECT x.val from data x, data y
GROUP BY x.val
HAVING SUM(SIGN(1-SIGN(y.val-x.val))) = (COUNT(*)+1)/2

নিশ্চিত করুন যে আপনার কলামগুলি ভালভাবে সূচিকৃত হয়েছে এবং সূচকটি ফিল্টারিং এবং বাছাইয়ের জন্য ব্যবহৃত হয়েছে। ব্যাখ্যা পরিকল্পনা সহ যাচাই করুন।

select count(*) from table --find the number of rows

"মিডিয়ান" সারি সংখ্যা গণনা করুন। হয়তো ব্যবহার করুন: median_row = floor(count / 2)

তারপরে এটি তালিকা থেকে বাছাই করুন:

select val from table order by val asc limit median_row,1

এটি আপনার পছন্দসই মান সহ একটি সারি ফিরিয়ে আনবে।

জ্যাকব


6
@rob আপনি কি সম্পাদনা করতে সাহায্য করতে পারেন? নাকি আমার কেবল ভেলক্রো দ্রবণকে প্রণাম করা উচিত? (কীভাবে অন্য কোনও সমাধানের
পিছনে পিছনে যায়

1
মনে রাখবেন এটি একটি "ক্রস জয়েন" করে, এটি বড় টেবিলগুলির জন্য খুব ধীর।
রিক জেমস

1
এই উত্তরটি এমনকি সারি সংখ্যার জন্য কিছুই দেয় না
কুট্টুমিয়া

এই উত্তরটি কিছু ডেটা সেটগুলির জন্য মোটেও কাজ করে না, উদাহরণস্বরূপ, 0.1, 0.1, 0.1, 2 মানের সাথে তুচ্ছ তথ্য সেট - সমস্ত মান পৃথক হলে এটি কাজ করবে, তবে মানগুলি যদি কাজ করে
কেম ম্যাসন

32

আমি খুঁজে পেয়েছি গ্রহণযোগ্য সমাধানটি আমার মাইএসকিউএল ইনস্টলের ক্ষেত্রে কাজ করে না, একটি খালি সেট ফিরে আসে, তবে এই কোয়েরিটি আমার জন্য পরীক্ষিত সমস্ত পরিস্থিতিতে আমার পক্ষে কাজ করেছে:

SELECT x.val from data x, data y
GROUP BY x.val
HAVING SUM(SIGN(1-SIGN(y.val-x.val)))/COUNT(*) > .5
LIMIT 1

1
একেবারে সঠিক, আমার সূচকযুক্ত টেবিলগুলিতে পুরোপুরি এবং খুব দ্রুত কাজ করে
রব

2
এটি এখানে সমস্ত উত্তরগুলির মধ্যে মাইএসকিএল-এর দ্রুততম সমাধান বলে মনে হচ্ছে, 200 মিলিতে টেবিলের এক মিলিয়ন রেকর্ডের সংক্ষিপ্ত
রব

3
@ ফ্র্যাঙ্ককনিজন: এটি একটি টেবিল থেকে দু'বার নির্বাচন করে। টেবিলের নাম dataএবং এটি দুটি নামে ব্যবহার করা হচ্ছে, xএবং y
ব্রায়ান

3
শুধু বলছিলাম যে আমি আমার মাইএসকিএলডিটি 33k সারি সহ একটি টেবিলে এই সঠিক ক্যোয়ারীটি দিয়ে থামিয়েছি ...
জেনোনাইট

1
এই ক্যোয়ারী এমনকি সংখ্যক সারির জন্য ভুল উত্তর দেয়
কুট্টুমিয়াহ

26

দুর্ভাগ্যক্রমে, TheJacobTaylor এর বা ভেলক্রোর উত্তরগুলি মাইএসকিউএলের বর্তমান সংস্করণগুলির জন্য সঠিক ফলাফল দেয় না।

উপরে থেকে ভেলক্রোর উত্তরটি নিকটে, তবে এটি এমনকি একাধিক সারি সহ ফলাফলের সেটগুলির জন্য সঠিকভাবে গণনা করে না। মিডিয়ানরা হয় সংজ্ঞায়িত হয় 1) বিজোড় সংখ্যাযুক্ত সেটগুলির মধ্যবর্তী সংখ্যা, বা 2) সমান সংখ্যক সেটগুলিতে দুটি মাঝারি সংখ্যার গড়।

সুতরাং, এখানে ভেলক্রোর সমাধানটি বিজোড় এবং সমান সংখ্যক সেট উভয়ই পরিচালনা করতে প্যাচ করেছে:

SELECT AVG(middle_values) AS 'median' FROM (
  SELECT t1.median_column AS 'middle_values' FROM
    (
      SELECT @row:=@row+1 as `row`, x.median_column
      FROM median_table AS x, (SELECT @row:=0) AS r
      WHERE 1
      -- put some where clause here
      ORDER BY x.median_column
    ) AS t1,
    (
      SELECT COUNT(*) as 'count'
      FROM median_table x
      WHERE 1
      -- put same where clause here
    ) AS t2
    -- the following condition will return 1 record for odd number sets, or 2 records for even number sets.
    WHERE t1.row >= t2.count/2 and t1.row <= ((t2.count/2) +1)) AS t3;

এটি ব্যবহার করতে, এই 3 টি সহজ পদক্ষেপগুলি অনুসরণ করুন:

  1. উপরের কোডটিতে "মিডিয়েন_ টেবিল" (2 টি ইভেন্ট) আপনার টেবিলের নামের সাথে প্রতিস্থাপন করুন
  2. আপনি যে কলামের নামটি খুঁজে পেতে চান তার সাথে "মধ্যক_ কলাম" (3 টি ইভেন্ট) প্রতিস্থাপন করুন
  3. আপনার যদি একটি শর্ত থাকে তবে আপনার যেখানে অবস্থার সাথে "WHERE 1" (2 টি ইভেন্ট) প্রতিস্থাপন করুন

এবং, আপনি স্ট্রিং মানগুলির মধ্যমা জন্য কী করবেন?
রিক জেমস

12

আমি একটি দ্রুত উপায় প্রস্তাব।

সারি গণনা পান:

SELECT CEIL(COUNT(*)/2) FROM data;

তারপরে একটি সাজানো সাবকোয়রিতে মাঝারি মানটি নিন:

SELECT max(val) FROM (SELECT val FROM data ORDER BY val limit @middlevalue) x;

আমি এলোমেলো সংখ্যার 5x10e6 ডেটাসেট দিয়ে এটি পরীক্ষা করেছি এবং এটি 10 ​​সেকেন্ডের নীচে মাঝারিটি খুঁজে পাবে।


3
কেন নয়: @ মিডলভ্যালু, 1
ব্রায়ান

1
আপনি কীভাবে আপনার দ্বিতীয় কোড ব্লকের মধ্যে প্রথম কোড ব্লকের পরিবর্তনশীল আউটপুট টানবেন?
ট্রিপ

3
যেমনটি, @ মিললভ্যালু কোথা থেকে আসে?
ট্রিপ

@ ব্রায়ান - আমি আপনার সাথে একমত, এটি আমার কাছে আরও বেশি অর্থবোধ করে। আপনি কি কখনও সেভাবে না করার কোনও কারণ খুঁজে পেয়েছেন?
শেন এন

5
এটি ভেরিয়েবল হিসাবে সীমাবদ্ধতার ধারাতে ব্যবহার করা যায় না বলে কাজ করে না।
কোডপেক

8

মাইএসকিউএল ডকুমেন্টেশনে এই পৃষ্ঠায় একটি মন্তব্যে নিম্নলিখিত পরামর্শ রয়েছে:

-- (mostly) High Performance scaling MEDIAN function per group
-- Median defined in http://en.wikipedia.org/wiki/Median
--
-- by Peter Hlavac
-- 06.11.2008
--
-- Example Table:

DROP table if exists table_median;
CREATE TABLE table_median (id INTEGER(11),val INTEGER(11));
COMMIT;


INSERT INTO table_median (id, val) VALUES
(1, 7), (1, 4), (1, 5), (1, 1), (1, 8), (1, 3), (1, 6),
(2, 4),
(3, 5), (3, 2),
(4, 5), (4, 12), (4, 1), (4, 7);



-- Calculating the MEDIAN
SELECT @a := 0;
SELECT
id,
AVG(val) AS MEDIAN
FROM (
SELECT
id,
val
FROM (
SELECT
-- Create an index n for every id
@a := (@a + 1) mod o.c AS shifted_n,
IF(@a mod o.c=0, o.c, @a) AS n,
o.id,
o.val,
-- the number of elements for every id
o.c
FROM (
SELECT
t_o.id,
val,
c
FROM
table_median t_o INNER JOIN
(SELECT
id,
COUNT(1) AS c
FROM
table_median
GROUP BY
id
) t2
ON (t2.id = t_o.id)
ORDER BY
t_o.id,val
) o
) a
WHERE
IF(
-- if there is an even number of elements
-- take the lower and the upper median
-- and use AVG(lower,upper)
c MOD 2 = 0,
n = c DIV 2 OR n = (c DIV 2)+1,

-- if its an odd number of elements
-- take the first if its only one element
-- or take the one in the middle
IF(
c = 1,
n = 1,
n = c DIV 2 + 1
)
)
) a
GROUP BY
id;

-- Explanation:
-- The Statement creates a helper table like
--
-- n id val count
-- ----------------
-- 1, 1, 1, 7
-- 2, 1, 3, 7
-- 3, 1, 4, 7
-- 4, 1, 5, 7
-- 5, 1, 6, 7
-- 6, 1, 7, 7
-- 7, 1, 8, 7
--
-- 1, 2, 4, 1

-- 1, 3, 2, 2
-- 2, 3, 5, 2
--
-- 1, 4, 1, 4
-- 2, 4, 5, 4
-- 3, 4, 7, 4
-- 4, 4, 12, 4


-- from there we can select the n-th element on the position: count div 2 + 1 

আইএমএইচও, এই পরিস্থিতিগুলির জন্য স্পষ্টতই সেরা এটি যেখানে আপনার একটি জটিল সাবসেট (গুলি) থেকে
মিডিয়েন

আমার জন্য ভাল কাজ করে। 5.6.14 মাইএসকিউএল কমিউনিটি সার্ভার। 11 এম রেকর্ডযুক্ত টেবিলে (ডিস্কে প্রায় 20 গিগাবাইট) দুটি প্রাথমিক সূচক (মডেল_আইডি, দাম) নেই। সারণীতে (পরিস্রাবণের পরে) এর জন্য মিডিয়ান গণনা করার জন্য আমাদের কাছে 500K রেকর্ড রয়েছে। ফলস্বরূপ আমাদের 30 কে রেকর্ড রয়েছে (মডেল_আইডি, মিডিয়ান_প্রাইস)। প্রশ্নের সময়কাল 1.5-2 সেকেন্ড। গতি আমার জন্য দ্রুত।
মিক্ল

7

এই mysql পরিসংখ্যানমূলক ফাংশন ইনস্টল করুন এবং ব্যবহার করুন : http://www.xarg.org/2012/07/statistical-function-in-mysql/

এরপরে, গণনা মিডিয়েন সহজ:

SELECT median(val) FROM data;

1
আমি নিজেই এটি চেষ্টা করেছি এবং এটির মূল্যের জন্য এটি ইনস্টল করা অত্যন্ত দ্রুত / সহজ ছিল এবং এটি গ্রুপিং সহ বিজ্ঞাপন হিসাবে কাজ করেছে, যেমন "নাম হিসাবে মধ্যম (এক্স) নির্বাচন করুন T1 গোষ্ঠী নাম" - গিথুব উত্স এখানে: github.com/infusion/udf_infusion
কেম ম্যাসন

6

উপরের বেশিরভাগ সমাধান কেবলমাত্র টেবিলের একটি ক্ষেত্রের জন্য কাজ করে, আপনাকে কোয়েরিতে অনেক ক্ষেত্রের জন্য মিডিয়ান (50 তম পার্সেন্টাইল) পেতে হতে পারে।

আমি এটি ব্যবহার:

SELECT CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(
 GROUP_CONCAT(field_name ORDER BY field_name SEPARATOR ','),
  ',', 50/100 * COUNT(*) + 1), ',', -1) AS DECIMAL) AS `Median`
FROM table_name;

আপনি "50" উদাহরণস্বরূপ যেকোন শতকের প্রতিস্থাপন করতে পারেন, খুব দক্ষ।

আপনার কাছে GROUP_CONCAT এর জন্য পর্যাপ্ত মেমরি রয়েছে তা নিশ্চিত করুন, আপনি এটি দিয়ে এটি পরিবর্তন করতে পারেন:

SET group_concat_max_len = 10485760; #10MB max length

আরও বিশদ: http : //web.performanceस्तa.com/metrics-tips-calculating-95th-99th-or-ny-percentil-with-single-mysql-query/


সচেতন থাকুন: এমনকি সংখ্যার মানের জন্য এটি দুটি মাঝারি মানের থেকেও বেশি লাগে। প্রতিক্রিয়ার সংখ্যার মানগুলির জন্য এটি মধ্যকের পরে পরবর্তী উচ্চতর মান গ্রহণ করে।
জিওর্ডানো

6

আমার কাছে হ্যাকারর্যাঙ্কে এই কোডটি নীচে পাওয়া গেছে এবং এটি বেশ সহজ এবং প্রতিটি ক্ষেত্রে কাজ করে।

SELECT M.MEDIAN_COL FROM MEDIAN_TABLE M WHERE  
  (SELECT COUNT(MEDIAN_COL) FROM MEDIAN_TABLE WHERE MEDIAN_COL < M.MEDIAN_COL ) = 
  (SELECT COUNT(MEDIAN_COL) FROM MEDIAN_TABLE WHERE MEDIAN_COL > M.MEDIAN_COL );

2
আমি বিশ্বাস করি এটি কেবলমাত্র একটি টেবিলের সাথে কাজ করে যাতে প্রবেশের সংখ্যা বিজোড়। এমনকি সংখ্যক এন্ট্রিগুলির ক্ষেত্রেও এটির সমস্যা হতে পারে।
ওয়াই চ্যাং

4

ভেলক্রোর জবাব বন্ধ করে দেওয়া, আপনারা যাদের জন্য অন্য প্যারামিটার দ্বারা গোষ্ঠীযুক্ত এমন কোনও কিছুতে মিডিয়ান করতে হবে:

নির্বাচন করুন grp_field , টি 1 ভাল FROM ( নির্বাচন করুন জিআরপি_ফিল্ড , @ রোাউনাম : = আইএফ (@ গুলি = জিআরপি_ফিল্ড , @ রোাউনাম + 1 , 0 ) এএস , @ এস : = আইএফ (@ এস = জিআরপি_ফিল্ড , @ @ রাউনম : = 0 , @ এস : = 0 ) অর্ডার বাই grp_field , 
         row_number
     গুলি , grp_field ) আঃ সেকেন্ড , Val
   থেকে ডেটা d , নির্বাচন করুন    (   Val
 ) যেমন T1 JOIN ( নির্বাচন grp_field , গণনা (*) যেমন total_rows
   থেকে ডেটা ঘ
   গ্রুপ বাই grp_field
 ) যেমন T2
 অন T1 grp_field = t2 জিআরপি_ফিল্ড
 যেখানে টি 1   
     ROW_NUMBER= তল (মোট_আরো / 2 ) +1 ;


3

আপনি ব্যবহারকারী-সংজ্ঞায়িত ফাংশন পাওয়া যে ব্যবহার করতে পারে এখানে


3
এটি সর্বাধিক দরকারী বলে মনে হচ্ছে তবে আমি অস্থির আলফা সফটওয়্যারটি ইনস্টল করতে চাই না যা আমার প্রোডাকশন সার্ভারে
মাইএসকিএল

6
সুতরাং আগ্রহের ক্রিয়াকলাপের জন্য তাদের উত্সগুলি অধ্যয়ন করুন, তাদের ঠিক করুন বা প্রয়োজনমতো তাদের সংশোধন করুন এবং একবার আপনার নিজের "স্থিতিশীল" এবং নন-আলফা সংস্করণটি ইনস্টল করুন similar একইভাবে কম প্রমাণিত কোড প্রস্তাবনাগুলি টুইট করার চেয়ে আরও খারাপ যে কী? আপনি এসও? -)
অ্যালেক্স মার্টেলি

3

একটি বিজোড় মান গণনা সম্পর্কে যত্ন নেয় - এই ক্ষেত্রে মাঝের দুটি মানের গড় দেয়।

SELECT AVG(val) FROM
  ( SELECT x.id, x.val from data x, data y
      GROUP BY x.id, x.val
      HAVING SUM(SIGN(1-SIGN(IF(y.val-x.val=0 AND x.id != y.id, SIGN(x.id-y.id), y.val-x.val)))) IN (ROUND((COUNT(*))/2), ROUND((COUNT(*)+1)/2))
  ) sq

2

আমার কোড, টেবিল বা অতিরিক্ত ভেরিয়েবল ছাড়াই দক্ষ:

SELECT
((SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(val order by val), ',', floor(1+((count(val)-1) / 2))), ',', -1))
+
(SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(val order by val), ',', ceiling(1+((count(val)-1) / 2))), ',', -1)))/2
as median
FROM table;

3
এটি কোনও পরিমাণের যথেষ্ট পরিমাণে ডেটা ব্যর্থ হবে কারণ GROUP_CONCAT1023 টি অক্ষরের মধ্যে সীমাবদ্ধ রয়েছে, যদিও এর মতো অন্য কোনও কার্যের অভ্যন্তরে ব্যবহৃত হয়।
রব ভ্যান ড্যাম

2

Allyচ্ছিকভাবে, আপনি এটি একটি সঞ্চিত পদ্ধতিতেও করতে পারেন:

DROP PROCEDURE IF EXISTS median;
DELIMITER //
CREATE PROCEDURE median (table_name VARCHAR(255), column_name VARCHAR(255), where_clause VARCHAR(255))
BEGIN
  -- Set default parameters
  IF where_clause IS NULL OR where_clause = '' THEN
    SET where_clause = 1;
  END IF;

  -- Prepare statement
  SET @sql = CONCAT(
    "SELECT AVG(middle_values) AS 'median' FROM (
      SELECT t1.", column_name, " AS 'middle_values' FROM
        (
          SELECT @row:=@row+1 as `row`, x.", column_name, "
          FROM ", table_name," AS x, (SELECT @row:=0) AS r
          WHERE ", where_clause, " ORDER BY x.", column_name, "
        ) AS t1,
        (
          SELECT COUNT(*) as 'count'
          FROM ", table_name, " x
          WHERE ", where_clause, "
        ) AS t2
        -- the following condition will return 1 record for odd number sets, or 2 records for even number sets.
        WHERE t1.row >= t2.count/2
          AND t1.row <= ((t2.count/2)+1)) AS t3
    ");

  -- Execute statement
  PREPARE stmt FROM @sql;
  EXECUTE stmt;
END//
DELIMITER ;


-- Sample usage:
-- median(table_name, column_name, where_condition);
CALL median('products', 'price', NULL);

এর জন্য ধন্যবাদ! ব্যবহারকারীর সচেতন হওয়া উচিত যে অনুপস্থিত মান (NULL) মান হিসাবে বিবেচিত হয়। এই সমস্যাটি এড়ানোর জন্য 'এক্স শালটি শূন্য নয়।
জিওর্ডানো

1
@giordano কোডের কোন লাইনে x IS NOT NULLযুক্ত করা উচিত?
প্রজেমিস্লা

1
@ প্রজেমাইস্লা রেমিন দুঃখিত, আমি আমার বক্তব্যে পরিষ্কার ছিলাম না এবং আমি বুঝতে পেরেছি যে এসপি ইতিমধ্যে অনুপস্থিত মানগুলির ক্ষেত্রে বিবেচনা করে। এসপি এই ভাবে বলে অভিহিত হই CALL median("table","x","x IS NOT NULL")
জিওরদানো

2

নীচে উপস্থাপিত আমার সমাধানটি টেবিল, পরিবর্তনশীল বা এমনকি সাব-কোয়েরি তৈরি না করে কেবল একটি ক্যোয়ারিতে কাজ করে। এছাড়াও, এটি আপনাকে গ্রুপ-বাই ক্যোয়ারিতে প্রতিটি গ্রুপের মধ্যস্থতা পেতে দেয় (এটি আমার প্রয়োজন যা প্রয়োজন!):

SELECT `columnA`, 
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(`columnB` ORDER BY `columnB`), ',', CEILING((COUNT(`columnB`)/2))), ',', -1) medianOfColumnB
FROM `tableC`
-- some where clause if you want
GROUP BY `columnA`;

এটি group_concat এবং সাবস্ট্রিং_ইন্ডেক্সের স্মার্ট ব্যবহারের কারণে কাজ করে।

তবে, বড় গ্রুপ_কনক্যাটকে অনুমতি দেওয়ার জন্য আপনাকে গ্রুপ_কনক্যাট_ম্যাক্স_লেনকে একটি উচ্চ মানের (1024 চর ডিফল্ট) সেট করতে হবে। আপনি এটির মতো এটি সেট করতে পারেন (বর্তমান স্ক্যুএল সেশনের জন্য):

SET SESSION group_concat_max_len = 10000; 
-- up to 4294967295 in 32-bits platform.

Group_concat_max_len আরও infos: https://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_group_concat_max_len


2

ভেলক্রোর উত্তরের উপর অন্য একটি রিফ, তবে একটি একক মধ্যবর্তী টেবিল ব্যবহার করে এবং গণনাটি পাওয়ার জন্য সারি সংখ্যার জন্য ব্যবহৃত ভেরিয়েবলটির সুবিধা গ্রহণ করে বরং এটি গণনা করার জন্য অতিরিক্ত কোয়েরি করে। গণনাটিও শুরু করে যাতে প্রথম সারিটি 0 টি সারি হয় যাতে সহজেই মেঝে এবং সিল ব্যবহার করে মধ্যম সারি নির্বাচন করতে পারে।

SELECT Avg(tmp.val) as median_val
    FROM (SELECT inTab.val, @rows := @rows + 1 as rowNum
              FROM data as inTab,  (SELECT @rows := -1) as init
              -- Replace with better where clause or delete
              WHERE 2 > 1
              ORDER BY inTab.val) as tmp
    WHERE tmp.rowNum in (Floor(@rows / 2), Ceil(@rows / 2));

2
SELECT 
    SUBSTRING_INDEX(
        SUBSTRING_INDEX(
            GROUP_CONCAT(field ORDER BY field),
            ',',
            ((
                ROUND(
                    LENGTH(GROUP_CONCAT(field)) - 
                    LENGTH(
                        REPLACE(
                            GROUP_CONCAT(field),
                            ',',
                            ''
                        )
                    )
                ) / 2) + 1
            )),
            ',',
            -1
        )
FROM
    table

উপরেরটি আমার পক্ষে কাজ করে বলে মনে হচ্ছে।


এটা তোলে মূল্যবোধের জোড় সংখ্যা জন্য মধ্যমা সঠিক মধ্যমা ফেরার নয়, উদাহরণস্বরূপ, {98,102,102,98}হয় 100কিন্তু আপনার কোড দেয় 102। বিজোড় সংখ্যাগুলির জন্য এটি দুর্দান্ত কাজ করেছে।
Nomiluks

1

আমি একটি দুটি প্রশ্নের পদ্ধতির ব্যবহার করেছি:

  • গণনা, সর্বনিম্ন, সর্বোচ্চ এবং গড় পেতে প্রথমটি
  • দ্বিতীয়টি (প্রস্তুত বিবৃতি) একটি "লিমিটেড @ গণনা / 2, 1" এবং "অর্ডার বাই .." সহ মধ্যমানের মান পেতে

এগুলি একটি ফাংশন ডিফেনের সাথে আবৃত থাকে, সুতরাং সমস্ত মানগুলি একটি কল থেকে ফিরে পাওয়া যায়।

যদি আপনার ব্যাপ্তি স্থির থাকে এবং আপনার ডেটা প্রায়শই পরিবর্তিত হয় না, তবে এই মানগুলি প্রতিরোধ / সঞ্চয় করা এবং প্রতিবার স্ক্র্যাচ থেকে অনুসন্ধানের পরিবর্তে সঞ্চিত মানগুলি ব্যবহার করা আরও দক্ষ।


1

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

function mysql_percentile($table, $column, $where, $percentile = 0.5) {

    $sql = "
            SELECT `t1`.`".$column."` as `percentile` FROM (
            SELECT @rownum:=@rownum+1 as `row_number`, `d`.`".$column."`
              FROM `".$table."` `d`,  (SELECT @rownum:=0) `r`
              ".$where."
              ORDER BY `d`.`".$column."`
            ) as `t1`, 
            (
              SELECT count(*) as `total_rows`
              FROM `".$table."` `d`
              ".$where."
            ) as `t2`
            WHERE 1
            AND `t1`.`row_number`=floor(`total_rows` * ".$percentile.")+1;
        ";

    $result = sql($sql, 1);

    if (!empty($result)) {
        return $result['percentile'];       
    } else {
        return 0;
    }

}

ব্যবহারটি খুব সহজ, আমার বর্তমান প্রকল্পের উদাহরণ:

...
$table = DBPRE."zip_".$slug;
$column = 'seconds';
$where = "WHERE `reached` = '1' AND `time` >= '".$start_time."'";

    $reaching['median'] = mysql_percentile($table, $column, $where, 0.5);
    $reaching['percentile25'] = mysql_percentile($table, $column, $where, 0.25);
    $reaching['percentile75'] = mysql_percentile($table, $column, $where, 0.75);
...

1

এখানে আমার উপায়। অবশ্যই, আপনি এটি একটি পদ্ধতিতে রাখতে পারেন :-)

SET @median_counter = (SELECT FLOOR(COUNT(*)/2) - 1 AS `median_counter` FROM `data`);

SET @median = CONCAT('SELECT `val` FROM `data` ORDER BY `val` LIMIT ', @median_counter, ', 1');

PREPARE median FROM @median;

EXECUTE median;

আপনি পরিবর্তনশীল এড়াতে পারতেন @median_counter, যদি আপনি এটিকে সাবস্টিটেড করেন:

SET @median = CONCAT( 'SELECT `val` FROM `data` ORDER BY `val` LIMIT ',
                      (SELECT FLOOR(COUNT(*)/2) - 1 AS `median_counter` FROM `data`),
                      ', 1'
                    );

PREPARE median FROM @median;

EXECUTE median;

1

এই উপায়ে subquery ব্যতীত সম এবং বিজোড় উভয় গণনা অন্তর্ভুক্ত বলে মনে হয়।

SELECT AVG(t1.x)
FROM table t1, table t2
GROUP BY t1.x
HAVING SUM(SIGN(t1.x - t2.x)) = 0

1

@ বব এর উত্তরের ভিত্তিতে, কিছু মানদণ্ড অনুসারে একাধিক মিডিয়ান ফিরিয়ে দেওয়ার ক্ষমতা রাখার জন্য এই কোয়েরিকে সাধারণীকরণ করেছে।

চিন্তা করুন, উদাহরণস্বরূপ, বছরের এক মাসের দ্বারা শ্রেণিবদ্ধ একটি গাড়ী লটে ব্যবহৃত গাড়ির জন্য মাঝারি বিক্রয় মূল্য।

SELECT 
    period, 
    AVG(middle_values) AS 'median' 
FROM (
    SELECT t1.sale_price AS 'middle_values', t1.row_num, t1.period, t2.count
    FROM (
        SELECT 
            @last_period:=@period AS 'last_period',
            @period:=DATE_FORMAT(sale_date, '%Y-%m') AS 'period',
            IF (@period<>@last_period, @row:=1, @row:=@row+1) as `row_num`, 
            x.sale_price
          FROM listings AS x, (SELECT @row:=0) AS r
          WHERE 1
            -- where criteria goes here
          ORDER BY DATE_FORMAT(sale_date, '%Y%m'), x.sale_price
        ) AS t1
    LEFT JOIN (  
          SELECT COUNT(*) as 'count', DATE_FORMAT(sale_date, '%Y-%m') AS 'period'
          FROM listings x
          WHERE 1
            -- same where criteria goes here
          GROUP BY DATE_FORMAT(sale_date, '%Y%m')
        ) AS t2
        ON t1.period = t2.period
    ) AS t3
WHERE 
    row_num >= (count/2) 
    AND row_num <= ((count/2) + 1)
GROUP BY t3.period
ORDER BY t3.period;

1

প্রায়শই, আমাদের কেবল পুরো টেবিলের জন্য নয়, আমাদের আইডি সম্পর্কিত সম্মিলনগুলির জন্য মিডিয়ান গণনা করতে হতে পারে। অন্য কথায়, আমাদের টেবিলের প্রতিটি আইডির জন্য মিডিয়ান গণনা করুন, যেখানে প্রতিটি আইডির অনেক রেকর্ড রয়েছে। (ভাল পারফরম্যান্স এবং অনেকগুলি এসকিউএল + সমতুল্যতার সমস্যাগুলি সমাধানের সমস্যাগুলিতে কাজ করে, বিভিন্ন মিডিয়ান-পদ্ধতিগুলির পারফরম্যান্স সম্পর্কে আরও https://sqlperformance.com/2012/08/t-sql-queries/median )

SELECT our_id, AVG(1.0 * our_val) as Median
FROM
( SELECT our_id, our_val, 
  COUNT(*) OVER (PARTITION BY our_id) AS cnt,
  ROW_NUMBER() OVER (PARTITION BY our_id ORDER BY our_val) AS rn
  FROM our_table
) AS x
WHERE rn IN ((cnt + 1)/2, (cnt + 2)/2) GROUP BY our_id;

আশা করি এটা সাহায্য করবে


এটি সেরা সমাধান। তবে বড় ডেটা সেটগুলির জন্য এটি ধীর হয়ে যাবে কারণ এটি প্রতিটি সেটের প্রতিটি আইটেমের জন্য পুনরায় গণনা করে। এটি দ্রুত করার জন্য সাব-কোয়েরি পৃথক করতে "COUNT (*)" লিখুন।
স্লাভা মুরগিন

1

মাইএসকিউএল ৮.০ সংস্করণ থেকে উইন্ডো ফাংশনকে সমর্থন করেছে, আপনি ব্যবহার করতে পারেন ROW_NUMBERবা DENSE_RANK( খেলা র্যাঙ্কিংয়ের মতো একই মানকে একই র‌্যাঙ্ক বরাদ্দ করার কারণে ব্যবহার করবেন নাRANK ):

SELECT AVG(t1.val) AS median_val
  FROM (SELECT val, 
               ROW_NUMBER() OVER(ORDER BY val) AS rownum
          FROM data) t1,
       (SELECT COUNT(*) AS num_records FROM data) t2
 WHERE t1.row_num IN
       (FLOOR((t2.num_records + 1) / 2), 
        FLOOR((t2.num_records + 2) / 2));

0

মাইএসকিউএলের যদি ROW_NUMBER থাকে, তবে মেডিয়ানটি (এই এসকিউএল সার্ভারের কোয়েরি দ্বারা অনুপ্রাণিত হবে):

WITH Numbered AS 
(
SELECT *, COUNT(*) OVER () AS Cnt,
    ROW_NUMBER() OVER (ORDER BY val) AS RowNum
FROM yourtable
)
SELECT id, val
FROM Numbered
WHERE RowNum IN ((Cnt+1)/2, (Cnt+2)/2)
;

আপনার যদি এমনকি একাধিক এন্ট্রি থাকে তবে IN ব্যবহার করা হয়।

আপনি যদি প্রতি গোষ্ঠীটির মধ্যবর্তী সন্ধান করতে চান, তবে আপনার ওভার ক্লজগুলিতে কেবলমাত্র বিভাগ দ্বারা পার্টিশন করুন।

হরণ করা


1
না, না ROW_NUMBER OVER, কোনও পক্ষ নেই , এর কোনওটিই নয়; এটি মাইএসকিউএল, পোস্টগ্র্রেএসকিউএল, আইবিএম ডিবি 2, এমএস এসকিউএল সার্ভারের মতো সত্যিকারের ডিবি ইঞ্জিন নয় এবং ;);
অ্যালেক্স মার্টেলি

0

পূর্ববর্তী সমস্তগুলি পড়ার পরে তারা আমার প্রকৃত প্রয়োজনের সাথে মেলে না তাই আমি আমার নিজের প্রয়োগ করেছি যার কোনও পদ্ধতির দরকার নেই বা বিবৃতি জটিল করতে হবে না, কেবল আমি GROUP_CONCAT যে কলামটিই মিডিয়ান অর্জন করতে চেয়েছিলাম এবং একটি COUNT ডিআইভির মাধ্যমে আবেদন করতে চেয়েছিলাম তার সমস্ত মানই পেয়েছি 2 আমি নীচের ক্যোয়ারির মতো তালিকার মাঝখানে থেকে মানটি বের করি:

(পিওএস হ'ল আমি যে কলামটির মাঝারি পেতে চাই তার নাম)

(query) SELECT
SUBSTRING_INDEX ( 
   SUBSTRING_INDEX ( 
       GROUP_CONCAT(pos ORDER BY CAST(pos AS SIGNED INTEGER) desc SEPARATOR ';') 
    , ';', COUNT(*)/2 ) 
, ';', -1 ) AS `pos_med`
FROM table_name
GROUP BY any_criterial

আমি আশা করি যে এই ওয়েবসাইটটি থেকে আমার পক্ষে অন্যান্য অনেক মন্তব্য ছিল সেভাবে কারও পক্ষে এটি কার্যকর হতে পারে।


0

সঠিক সারি গণনা জেনে আপনি এই ক্যোয়ারীটি ব্যবহার করতে পারেন:

SELECT <value> AS VAL FROM <table> ORDER BY VAL LIMIT 1 OFFSET <half>

কোথায় <half> = ceiling(<size> / 2.0) - 1


0

আমার প্রায় 1 বিলিয়ন সারি রয়েছে এমন একটি ডাটাবেস রয়েছে যা আমাদের সেটে মধ্য বয়সটি নির্ধারণ করতে হবে। বিলিয়ন সারি বাছাই করা শক্ত, তবে যদি আপনি পৃথক মানগুলি সন্ধান করতে পারেন (বয়সগুলি 0 থেকে 100 এর মধ্যে থাকে) তবে আপনি এই তালিকাটি বাছাই করতে পারেন এবং নীচের মতো আপনি চান এমন কোনও পার্সেন্টাইল সন্ধান করতে কিছু গাণিতিক যাদু ব্যবহার করতে পারেন:

with rawData(count_value) as
(
    select p.YEAR_OF_BIRTH
        from dbo.PERSON p
),
overallStats (avg_value, stdev_value, min_value, max_value, total) as
(
  select avg(1.0 * count_value) as avg_value,
    stdev(count_value) as stdev_value,
    min(count_value) as min_value,
    max(count_value) as max_value,
    count(*) as total
  from rawData
),
aggData (count_value, total, accumulated) as
(
  select count_value, 
    count(*) as total, 
        SUM(count(*)) OVER (ORDER BY count_value ROWS UNBOUNDED PRECEDING) as accumulated
  FROM rawData
  group by count_value
)
select o.total as count_value,
  o.min_value,
    o.max_value,
    o.avg_value,
    o.stdev_value,
    MIN(case when d.accumulated >= .50 * o.total then count_value else o.max_value end) as median_value,
    MIN(case when d.accumulated >= .10 * o.total then count_value else o.max_value end) as p10_value,
    MIN(case when d.accumulated >= .25 * o.total then count_value else o.max_value end) as p25_value,
    MIN(case when d.accumulated >= .75 * o.total then count_value else o.max_value end) as p75_value,
    MIN(case when d.accumulated >= .90 * o.total then count_value else o.max_value end) as p90_value
from aggData d
cross apply overallStats o
GROUP BY o.total, o.min_value, o.max_value, o.avg_value, o.stdev_value
;

এই ক্যোয়ারীটি আপনার ডিবি সমর্থনকারী উইন্ডো ফাংশনগুলির উপর নির্ভর করে (রউস আনবাউন্ডেড পূর্বনির্ধারিত সহ) তবে যদি আপনার কাছে না থাকে তবে অ্যাগ্রিটাটা সিটিইকে নিজের সাথে যুক্ত করা এবং পূর্বের মোট যোগফলগুলি 'জমে থাকা' কলামে একত্রিত করা কোনটি নির্ধারণের জন্য ব্যবহৃত হয়? মান উল্লেখ করা আছে। উপরের নমুনা পি 10, পি 25, প 50 (মিডিয়ান), পি 75 এবং পি 90 গণনা করে।

-Chris


0

থেকে নেওয়া: http://mdb-blog.blogspot.com/2015/06/mysql-find-median-nth-element-without.html

আমি যোগ না করেই অন্য উপায়ের পরামর্শ দেব , তবে স্ট্রিং নিয়ে কাজ করব

আমি এটি বড় ডেটা সহ টেবিলগুলির সাথে চেক করিনি, তবে ছোট / মাঝারি টেবিলগুলি এটি ঠিক কাজ করে।

এখানে ভাল জিনিস, এটি গ্রুপিংয়ের মাধ্যমেও কাজ করে যাতে এটি বেশ কয়েকটি আইটেমের জন্য মধ্যম ফিরিয়ে দিতে পারে।

পরীক্ষার টেবিলের জন্য এখানে টেস্ট কোড দেওয়া আছে:

DROP TABLE test.test_median
CREATE TABLE test.test_median AS
SELECT 'book' AS grp, 4 AS val UNION ALL
SELECT 'book', 7 UNION ALL
SELECT 'book', 2 UNION ALL
SELECT 'book', 2 UNION ALL
SELECT 'book', 9 UNION ALL
SELECT 'book', 8 UNION ALL
SELECT 'book', 3 UNION ALL

SELECT 'note', 11 UNION ALL

SELECT 'bike', 22 UNION ALL
SELECT 'bike', 26 

এবং প্রতিটি গ্রুপের জন্য মিডিয়েন সন্ধানের কোড:

SELECT grp,
         SUBSTRING_INDEX( SUBSTRING_INDEX( GROUP_CONCAT(val ORDER BY val), ',', COUNT(*)/2 ), ',', -1) as the_median,
         GROUP_CONCAT(val ORDER BY val) as all_vals_for_debug
FROM test.test_median
GROUP BY grp

আউটপুট:

grp | the_median| all_vals_for_debug
bike| 22        | 22,26
book| 4         | 2,2,3,4,7,8,9
note| 11        | 11

আপনি কি মনে করেন না যে,, 22,26} of এর মিডিয়ান 24 হওয়া উচিত?
নমিলুকস

0

কিছু ক্ষেত্রে মিডিয়ান নিম্নলিখিত হিসাবে গণনা করা হয়:

যখন মান দ্বারা আদেশ করা হয় তখন "মধ্যমা" হ'ল সংখ্যার তালিকার "মাঝারি" মান। এমনকি গণনা সেটগুলির জন্য, মাঝারি দুটি মধ্যম মানের গড় হয় । আমি এটির জন্য একটি সাধারণ কোড তৈরি করেছি:

$midValue = 0;
$rowCount = "SELECT count(*) as count {$from} {$where}";

$even = FALSE;
$offset = 1;
$medianRow = floor($rowCount / 2);
if ($rowCount % 2 == 0 && !empty($medianRow)) {
  $even = TRUE;
  $offset++;
  $medianRow--;
}

$medianValue = "SELECT column as median 
               {$fromClause} {$whereClause} 
               ORDER BY median 
               LIMIT {$medianRow},{$offset}";

$medianValDAO = db_query($medianValue);
while ($medianValDAO->fetch()) {
  if ($even) {
    $midValue = $midValue + $medianValDAO->median;
  }
  else {
    $median = $medianValDAO->median;
  }
}
if ($even) {
  $median = $midValue / 2;
}
return $median;

Returned মিডিয়ান ফিরিয়ে দেওয়া প্রয়োজনীয় ফলাফল হবে :-)

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.