পোস্টগ্রিসে দ্রুত এলোমেলো সারি নির্বাচন


98

আমার পোস্টগ্রিসে একটি টেবিল রয়েছে যাতে কয়েক মিলিয়ন সারি রয়েছে। আমি ইন্টারনেটে চেক করেছি এবং আমি নিম্নলিখিতটি পেয়েছি

SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;

এটি কাজ করে, তবে এটি সত্যিই ধীর ... এই কোয়েরিটি করার অন্য কোনও উপায় আছে বা সমস্ত টেবিল না পড়েই এলোমেলো সারিটি নির্বাচন করার সরাসরি উপায়? যাইহোক 'মাইড' একটি পূর্ণসংখ্যা তবে এটি একটি খালি ক্ষেত্র হতে পারে।


4
আপনি যদি একাধিক এলোমেলো সারি নির্বাচন করতে চান তবে এই প্রশ্নটি দেখুন: stackoverflow.com/q/8674718/247696
ফ্লিম

উত্তর:


99

আপনি OFFSETযেমন হিসাবে পরীক্ষা করতে চান

SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;

এতে Nসারি সংখ্যা mytableSELECT COUNT(*)এর মান বের করার জন্য আপনাকে প্রথমে একটি করতে হবে N

আপডেট (অ্যান্টনি হ্যাচকিন্স দ্বারা)

আপনি অবশ্যই floorএখানে ব্যবহার করুন:

SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;

2 সারি একটি সারণী বিবেচনা করুন; random()*Nউত্পন্ন করে 0 <= x < 2এবং উদাহরণস্বরূপ SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;নিকটতম ইন-তে অন্তর্ভুক্ত বৃত্তাকার কারণে 0 টি সারি দেয় returns


এন এর চেয়ে কম এন ব্যবহার করা কি বোধগম্য?, SELECT COUNT(*)মানে, টেবিলের সমস্ত মান ব্যবহার না করে কেবল তার একটি অংশ?
জুয়ান

@ জুয়ান এটি আপনার প্রয়োজনীয়তার উপর নির্ভর করে।
এনপিই

EXPLAIN SELECT ...এন এর বিভিন্ন মান সহ ব্যবহার করে ক্যোয়ারির জন্য একই দাম দেয়, তবে অনুমান করি যে এন এর সর্বাধিক মানটি পাওয়া ভাল
জুয়ান

4
নীচে আমার উত্তরে একটি
বাগফিক্স

4
এটি একটি ত্রুটি দ্বারা বন্ধ আছে। এটি কখনই প্রথম সারিতে ফিরে আসবে না এবং ত্রুটি 1 / COUNT (*) উত্পন্ন করবে কারণ এটি শেষ সারির পরে সারিটি ফেরত দেওয়ার চেষ্টা করবে।
আয়ান

62

পোস্টগ্রিসকিউএল 9.5 আরও দ্রুততর নমুনা নির্বাচনের জন্য একটি নতুন পদ্ধতির প্রবর্তন করেছে: টেবিলসাম্পল

বাক্য গঠনটি হ'ল

SELECT * FROM my_table TABLESAMPLE BERNOULLI(percentage);
SELECT * FROM my_table TABLESAMPLE SYSTEM(percentage);

আপনি যদি কেবল একটি সারি বাছাই করতে চান তবে এটি সর্বোত্তম সমাধান নয়, কারণ সঠিক শতাংশের গণনা করার জন্য আপনাকে সারণির COUNT টি জানতে হবে।

একটি ধীর গতিতে COUNT এড়াতে এবং 1 সারি থেকে কোটি কোটি সারি সারণীগুলির জন্য দ্রুত টেবিলসাম্পল ব্যবহার করতে, আপনি এটি করতে পারেন:

 SELECT * FROM my_table TABLESAMPLE SYSTEM(0.000001) LIMIT 1;
 -- if you got no result:
 SELECT * FROM my_table TABLESAMPLE SYSTEM(0.00001) LIMIT 1;
 -- if you got no result:
 SELECT * FROM my_table TABLESAMPLE SYSTEM(0.0001) LIMIT 1;
 -- if you got no result:
 SELECT * FROM my_table TABLESAMPLE SYSTEM(0.001) LIMIT 1;
 ...

এটি এত মার্জিত নাও লাগতে পারে তবে অন্য উত্তরগুলির চেয়ে সম্ভবত দ্রুত।

আপনি বার্নুলি ওডার সিস্টেমটি ব্যবহার করতে চান কিনা তা সিদ্ধান্ত নিতে, http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/ এ পার্থক্য সম্পর্কে পড়ুন


4
এটি অন্য যে কোনও উত্তরের চেয়ে অনেক দ্রুত এবং সহজ - এটির শীর্ষে থাকা উচিত।
হেডেন শিফ

4
আপনি কেন গণনা পেতে একটি subquery ব্যবহার করতে পারবেন না? SELECT * FROM my_table TABLESAMPLE SYSTEM(SELECT 1/COUNT(*) FROM my_table) LIMIT 1;?
মেশিনঘোস্ট 12'19

4
@ ম্যাচাইনহোস্ট "একটি ধীর গতিতে COUNT এড়ানোর জন্য ..." ... যদি আপনার ডেটা এত ছোট হয় যে আপনি উপযুক্ত সময়ে গণনা করতে পারেন তবে এর জন্য যান! :-)
আলফোনাক্স

4
SELECT reltuples FROM pg_class WHERE relname = 'my_table'গণনা অনুমানের জন্য @ ম্যাচাইনহোস্ট ব্যবহার করুন ।
হায়েনেক-পিচি- ভ্যাচোডিল

@ হায়েনেক-পিচি-ভ্যাচোডিল খুব ভাল ইনপুট! অনুমানটি পুরানো না হওয়ার জন্য, এটি সম্প্রতি ভ্যাকুয়াম অ্যানালাইজড মৌমাছি রাখতে হবে .. তবে একটি ভাল ডাটাবেসটি যাইহোক সঠিকভাবে বিশ্লেষণ করা উচিত .. এবং এটি সমস্ত নির্দিষ্ট ব্যবহারের ক্ষেত্রে নির্ভর করে। সাধারণত বিশাল টেবিলগুলি এত দ্রুত বৃদ্ধি পায় না ... ধন্যবাদ!
আলফোনাক্স

34

আমি একটি subquery সঙ্গে এটি চেষ্টা করেছিলাম এবং এটি ভাল কাজ করে। অফসেট, কমপক্ষে Postgresql v8.4.4 এ সূক্ষ্ম কাজ করে।

select * from mytable offset random() * (select count(*) from mytable) limit 1 ;

আসলে, v8.4 এটি কাজ করার জন্য প্রয়োজনীয়, <= 8.3 এর জন্য কাজ করে না।
অ্যান্টনি হ্যাচকিন্স

4
নীচে আমার উত্তরে একটি
বাগফিক্স

32

আপনার ব্যবহার করা দরকার floor:

SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;

2 সারি একটি সারণী বিবেচনা করুন; random()*N0 <= x <2 উত্পন্ন করে এবং উদাহরণস্বরূপ SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;নিকটতম ইন-তে অন্তর্ভুক্ত বৃত্তাকার কারণে 0 টি সারি দেয় returns
অ্যান্টনি হ্যাচকিন্স

দুর্ভাগ্যক্রমে আপনি যদি উচ্চতর লিমিটেড ব্যবহার করতে চান তবে এটি কাজ করে না ... আমাকে 3 টি আইটেম পেতে হবে যাতে আমার দ্বারা র‌্যান্ডম () সিনট্যাক্স দ্বারা অর্ডার ব্যবহার করা প্রয়োজন।
অ্যালেক্সিস উইলক

4
পরপর তিনটি প্রশ্ন এখনও একের চেয়ে দ্রুততর হবে order by random(), প্রায় 3*O(N) < O(NlogN)- সূচকগুলির কারণে রিলাইফের পরিসংখ্যানগুলি কিছুটা আলাদা হবে।
অ্যান্টনি হ্যাচকিন্স

আমার সমস্যাটি হ'ল 3 টি আইটেমের স্বতন্ত্র এবং একটি হওয়া দরকার WHERE myid NOT IN (1st-myid)এবং WHERE myid NOT IN (1st-myid, 2nd-myid)যেহেতু সিদ্ধান্তটি অফসেটের দ্বারা সিদ্ধান্ত নেওয়া হয় কাজ করবে না। হুমম ... আমি অনুমান করি যে আমি দ্বিতীয়টি এবং তৃতীয় নির্বাচনকে এন এবং 1 এবং 2 দ্বারা হ্রাস করতে পারব।
অ্যালেক্সিস উইলক

আমাকে বা কেন ব্যবহার করতে হবে তার উত্তর দিয়ে আপনি বা কেউ এই উত্তরটি প্রসারিত করতে পারেন floor()? এটি কি সুবিধা দেয়?
এডিটিসি

14

কিছু ভিন্ন বিকল্পের জন্য এই লিঙ্কটি দেখুন। http://www.depesz.com/index.php/2007/09/16/my- خصوصیاتts-on-getting-random-row/

হালনাগাদ: (এ। হ্যাচকিনস)

নীচে (খুব) দীর্ঘ নিবন্ধটির সংক্ষিপ্তসার রয়েছে।

লেখক চারটি পদ্ধতির তালিকা দিয়েছেন:

1) ORDER BY random() LIMIT 1; - ধীর

2) ORDER BY id where id>=random()*N LIMIT 1- ফাঁক থাকলে নন-ইউনিফর্ম

3) এলোমেলো কলাম - প্রতিটি এবং পরে আপডেট করা প্রয়োজন

4) কাস্টম এলোমেলো সমষ্টি - ধূর্ত পদ্ধতিটি ধীর হতে পারে: এলোমেলো () N বার উত্পন্ন করা দরকার

এবং ব্যবহার করে পদ্ধতি # 2 উন্নত করার পরামর্শ দেয়

5) ORDER BY id where id=random()*N LIMIT 1 ফলাফল খালি হলে পরবর্তী প্রয়োজনীয়তার সাথে।


আমি অবাক হয়েছি কেন তারা অফসেটটি কভার করেনি? একটি অর্ডার ব্যবহার করা কেবল এলোমেলো সারি পাওয়ার জন্য প্রশ্নের বাইরে। ভাগ্যক্রমে, অফসেট উত্তরগুলিতে ভালভাবে কভার করা আছে।
androidguy

4

এলোমেলোভাবে সারি আনার সহজ ও দ্রুততম উপায় হ'ল tsm_system_rowsএক্সটেনশনটি ব্যবহার করা :

CREATE EXTENSION IF NOT EXISTS tsm_system_rows;

তারপরে আপনি যে সারিটি চান তার সঠিক সংখ্যাটি নির্বাচন করতে পারেন:

SELECT myid  FROM mytable TABLESAMPLE SYSTEM_ROWS(1);

এটি পোস্টগ্রিসকিউএল 9.5 এবং এর পরে পাওয়া যায়।

দেখুন: https://www.postgresql.org/docs/current/static/tsm-sstm-rows.html


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

4
হ্যাঁ এটি ডকুমেন্টেশনে স্পষ্টভাবে ব্যাখ্যা করা হয়েছে (উপরের লিঙ্কে): the বিল্ট-ইন সিস্টেমে স্যাম্পলিং পদ্ধতির মতো, SYSTEM_ROWS ব্লক-স্তরের নমুনা সম্পাদন করে, যাতে নমুনা সম্পূর্ণরূপে এলোমেলো নয় তবে ক্লাস্টারিং এফেক্টের সাথে সম্পর্কিত হতে পারে, বিশেষত যদি কেবলমাত্র একটি ছোট সারি সংখ্যা অনুরোধ করা হয়। । আপনার যদি একটি ছোট ডেটাসেট থাকে তবে ORDER BY random() LIMIT 1;তা যথেষ্ট দ্রুত হওয়া উচিত।
দ্যামিয়েন

আমি দেখেছি। যে কেউ লিঙ্কটি ক্লিক করেন না বা ভবিষ্যতে যদি লিঙ্কটি মারা যায় তবে কেবল এটিই পরিষ্কার করে দিতে চেয়েছিলেন।
বেন অউবিন

4
এছাড়াও লক্ষণীয় যে এটি কেবল কোনও টেবিলের বাইরে এলোমেলো সারি নির্বাচন করার জন্য এবং তারপরে ফিল্টারিংয়ের জন্য কাজ করবে, বিরোধী / কোয়েরি চালানোর তুলনায় এবং তারপরে এলোমেলোভাবে একটি বা কিছু রেকর্ড বাছাইয়ের জন্য।
নাম

3

আমি ছাড়া খুব দ্রুত সমাধান নিয়ে এসেছি TABLESAMPLE। এর চেয়ে অনেক বেশি দ্রুত OFFSET random()*N LIMIT 1। এটি এমনকি টেবিল গণনা প্রয়োজন হয় না।

ধারণাটি হল এলোমেলো তবে অনুমানযোগ্য ডেটা সহ একটি এক্সপ্রেশন সূচক তৈরি করা, উদাহরণস্বরূপ md5(primary key)

1M সারি নমুনা ডেটা সহ এখানে একটি পরীক্ষা দেওয়া হয়েছে:

create table randtest (id serial primary key, data int not null);

insert into randtest (data) select (random()*1000000)::int from generate_series(1,1000000);

create index randtest_md5_id_idx on randtest (md5(id::text));

explain analyze
select * from randtest where md5(id::text)>md5(random()::text)
order by md5(id::text) limit 1;

ফলাফল:

 Limit  (cost=0.42..0.68 rows=1 width=8) (actual time=6.219..6.220 rows=1 loops=1)
   ->  Index Scan using randtest_md5_id_idx on randtest  (cost=0.42..84040.42 rows=333333 width=8) (actual time=6.217..6.217 rows=1 loops=1)
         Filter: (md5((id)::text) > md5((random())::text))
         Rows Removed by Filter: 1831
 Total runtime: 6.245 ms

এই কোয়েরিটি মাঝে মাঝে (প্রায় 1 / সংখ্যা_আফ_আর সম্ভাব্যতার সাথে) 0 টি সারি ফেরত দিতে পারে, সুতরাং এটি পরীক্ষা করে পুনরায় চালু করা দরকার। এছাড়াও সম্ভাবনাগুলি হুবহু নয় - কিছু সারি অন্যদের চেয়ে বেশি সম্ভাব্য।

তুলনার জন্য:

explain analyze SELECT id FROM randtest OFFSET random()*1000000 LIMIT 1;

ফলাফলগুলি ব্যাপকভাবে পরিবর্তিত হয়, তবে এটি বেশ খারাপ হতে পারে:

 Limit  (cost=1442.50..1442.51 rows=1 width=4) (actual time=179.183..179.184 rows=1 loops=1)
   ->  Seq Scan on randtest  (cost=0.00..14425.00 rows=1000000 width=4) (actual time=0.016..134.835 rows=915702 loops=1)
 Total runtime: 179.211 ms
(3 rows)

4
দ্রুত, হ্যাঁ সত্যিই এলোমেলো, না। একটি এমডি 5 মান যা অন্য বিদ্যমান মানের পরে পরবর্তী বৃহত্তর মান হিসাবে ঘটে তা খুব পাতলা করার সুযোগ রয়েছে, যখন সংখ্যার জায়গার বড় ব্যবধানের পরে মানগুলির মধ্যে অনেক বড় সুযোগ থাকে (এর মধ্যে সম্ভাব্য মানের সংখ্যার চেয়ে বড়) । ফলাফল বিতরণ এলোমেলো নয়।
এরউইন ব্র্যান্ডস্টেটার

খুব আকর্ষণীয়, এটি কি লটারির মতো কোয়েরির ব্যবহারে কাজ করতে পারে: ক্যোয়ারীতে অবশ্যই সমস্ত উপলব্ধ টিকিট দেখতে হবে এবং এলোমেলোভাবে কেবলমাত্র একটি একক টিকিট ফিরতে হবে। এছাড়াও আমি কি আপনার কৌশলটি দিয়ে একটি নিরাশাবাদী লক ব্যবহার করতে পারি (আপডেটের জন্য ... নির্বাচন করুন)?
ম্যাথিউ

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