একটি স্ক্লাইট টেবিল থেকে এলোমেলো সারি নির্বাচন করুন


119

sqliteনিম্নলিখিত স্কিমা সহ আমার একটি টেবিল রয়েছে:

CREATE TABLE foo (bar VARCHAR)

আমি স্ট্রিং হিসাবে স্ট্রিংয়ের তালিকার জন্য এই টেবিলটি ব্যবহার করছি।

আমি এই টেবিল থেকে এলোমেলো সারিটি কীভাবে নির্বাচন করব?


উত্তর:


213

একটি এসকিউএল টেবিল থেকে একটি এলোমেলো সারি নির্বাচন করার জন্য একবার দেখুন

SELECT * FROM table ORDER BY RANDOM() LIMIT 1;

1
কীভাবে এই সমাধানটিকে একটি যোগদানের ক্ষেত্রে প্রসারিত করবেন? ব্যবহার করার সময় SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;আমি সর্বদা একই সারি পাই।
হেলমুট গ্রোনে

এলোমেলো সংখ্যার বীজ দেওয়া কি সম্ভব? উদাহরণস্বরূপ আজকের দুপুরে ইউনিক্স এপোকাসের সাথে বদ্ধ দিনের বুক তাই কোয়েরিটি একাধিকবার চালানো হলেও সারা দিন একই বইটি দেখায়। হ্যাঁ আমি জানি এই ব্যবহারের ক্ষেত্রে ক্যাশিং আরও কার্যকরী কেবল উদাহরণ।
danielson317

FWIW আমার প্রশ্নের উত্তর এখানে দেওয়া আছে। এবং উত্তর আপনি এলোমেলো সংখ্যা বীজ করতে পারবেন না। stackoverflow.com/questions/24256258/...
danielson317

31

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

যদি আপনার সারিগুলি পরিবর্তে প্যাক করা থাকে (যেমন কয়েকটি মুছা), তবে আপনি নিম্নলিখিতটি করতে পারেন ( মন্তব্যে বর্ণিত হিসাবে আরও ভাল পারফরম্যান্স দেওয়ার (select max(rowid) from foo)+1পরিবর্তে ব্যবহার করে max(rowid)+1):

select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));

আপনার যদি গর্ত থাকে তবে আপনি মাঝে মাঝে একটি অ-অযৌক্তিক রোয়েড নির্বাচন করার চেষ্টা করবেন এবং নির্বাচনটি খালি ফলাফল সেটটি ফিরিয়ে দেবে। যদি এটি গ্রহণযোগ্য না হয় তবে আপনি এটির মতো একটি ডিফল্ট মান প্রদান করতে পারেন:

select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)) or rowid = (select max(rowid) from node) order by rowid limit 1;

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

তবুও অন্য সমাধান, যদি আপনি প্রায়শই প্রচুর গর্তযুক্ত একটি টেবিল থেকে এলোমেলো জিনিস নির্বাচন করেন, তবে আপনি এলোমেলো ক্রমে সাজানো মূল টেবিলের সারিগুলিতে একটি টেবিল তৈরি করতে চাইতে পারেন:

create table random_foo(foo_id);

তারপরে, সাময়িকী, টেবিলটি এলোমেলো_ফুতে আবার পূরণ করুন

delete from random_foo;
insert into random_foo select id from foo;

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

তবুও, অন্য একটি উপায়, যা আমি সম্প্রতি একটি মেলিং তালিকায় পেয়েছি তা হ'ল মুছে ফেলার জন্য একটি ট্রিগার স্থাপন করা যাতে সর্বাধিক রোউইডযুক্ত সারিটি বর্তমান মুছে ফেলা সারিটিতে সরিয়ে নেওয়া হয়, যাতে কোনও ছিদ্র না থাকে।

সবশেষে, নোট করুন যে রোউইড এবং একটি পূর্ণসংখ্যার প্রাথমিক কী স্বতঃসংশোধনের আচরণটি অভিন্ন নয় (সারিউডের সাথে, যখন একটি নতুন সারি সন্নিবেশ করা হয়, সর্বাধিক (রোউইড) +1 নির্বাচন করা হয়, তবে এটি হিজেস্ট-মান-সর্বদা-দেখা +1 হয় একটি প্রাথমিক কী), সুতরাং শেষ সমাধানটি এলোমেলো_ফুতে স্বতঃসংশোধনের সাথে কাজ করবে না, তবে অন্যান্য পদ্ধতিগুলি তা করবে।


ভালো লেগেছে আমি শুধু একটি মেইলিং লিস্ট দেখেছি, পরিবর্তে ফলব্যাক পদ্ধতি (পদ্ধতি 2) থাকার, আপনি শুধু ব্যবহার করতে পারেন rowid> = [র্যান্ডম] পরিবর্তে = এর, কিন্তু এটি আসলে slugissingly ধীর পদ্ধতি 2. সাথে তুলনা করা হয়
সুজান্ন Dupéron

3
এটি একটি দুর্দান্ত উত্তর; তবে এটির একটি সমস্যা আছে। SELECT max(rowid) + 1একটি ধীর ক্যোয়ারী হবে - এটির জন্য একটি পূর্ণ টেবিল স্ক্যান প্রয়োজন। স্ক্লাইট শুধুমাত্র ক্যোরিটি অনুকূল করে SELECT max(rowid)। সুতরাং, এই উত্তরটি আরও উন্নত হবে: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)); আরও তথ্যের জন্য এটি দেখুন: sqlite.1065341.n5.nabble.com/…
দাসল

19

আপনার ক্যোয়ারিতে আপনাকে "র্যান্ডম বাই অর্ডার ()" লাগানো দরকার ।

উদাহরণ:

select * from quest order by RANDOM();

আসুন একটি সম্পূর্ণ উদাহরণ দেখুন

  1. একটি টেবিল তৈরি করুন:
CREATE TABLE  quest  (
    id  INTEGER PRIMARY KEY AUTOINCREMENT,
    quest TEXT NOT NULL,
    resp_id INTEGER NOT NULL
);

কিছু মান সন্নিবেশ করা হচ্ছে:

insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24);

একটি ডিফল্ট নির্বাচন:

select * from quest;

| id |   quest  | resp_id |
   1     1024/4       6
   2     256/2       12
   3     128/1       24
--

একটি নির্বাচন করুন এলোমেলো:

select * from quest order by RANDOM();
| id |   quest  | resp_id |
   3     128/1       24
   1     1024/4       6
   2     256/2       12
--
* প্রতিবার আপনি নির্বাচন করুন, ক্রম পৃথক হবে।

আপনি যদি চান তবে কেবল একটি সারি

select * from quest order by RANDOM() LIMIT 1;
| id |   quest  | resp_id |
   2     256/2       12
--
* প্রতিবার আপনি নির্বাচন করুন, রিটার্ন ভিন্ন হবে।


কেবলমাত্র কোডের উত্তরগুলি নিষিদ্ধ করা হলেও, দয়া করে বুঝতে পারেন যে এটি ভিড়-উত্সাহের চেয়ে বরং প্রশ্নোত্তর সম্প্রদায়, এবং সাধারণত, ওপি যদি উত্তরটিকে উত্তর হিসাবে পোস্ট করা হয় তা বুঝতে পারত তবে সে উপস্থিত হত তার নিজের অনুরূপ সমাধান সহ, এবং প্রথম দিকে কোনও প্রশ্ন পোস্ট করত না। যেমন, প্রদান করুন প্রেক্ষাপটে ব্যাখ্যা করার মাধ্যমে আপনার উত্তর এবং / অথবা কোডে কিভাবে এবং / অথবা কেন এটি কাজ করে।
XenoRo

2
আমি এই সমাধানটি পছন্দ করি, যেহেতু এটি আমাকে এন লাইনগুলি অনুসন্ধান করার অনুমতি দেয়। আমার ক্ষেত্রে, ডাটাবেস থেকে আমার 100 টি এলোমেলো নমুনার প্রয়োজন ছিল - লিমিটেড 100 এর সাথে মিলিত অর্ডারের বাই র্যান্ডম () ঠিক এটি করে।
এমএনআর

17

কি সম্পর্কে:

SELECT COUNT(*) AS n FROM foo;

তারপর একটি র্যান্ডম সংখ্যা নির্বাচন করুন মিটার মধ্যে [0, এন) এবং

SELECT * FROM foo LIMIT 1 OFFSET m;

এমনকি আপনি প্রথম সংখ্যাটি ( এন ) কোথাও সংরক্ষণ করতে পারেন এবং ডাটাবেস গণনা পরিবর্তিত হলেই এটি আপডেট করতে পারেন। এইভাবে আপনাকে প্রতিবার SELECT COUNT করতে হবে না।


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

একটি কৌতূহলজনক বিষয় লক্ষণীয় যে OFFSETঅফসেটের আকারের উপর নির্ভর করে এটির সন্ধানের জন্য প্রয়োজনীয় সময়টি মনে হচ্ছে - সারি 2 দ্রুত, সারি 2 মিলিয়ন কিছু সময় নেয়, এমনকি যখন সমস্ত ডেটা স্থির-আকার হয় এবং এটি এটি সরাসরি অনুসন্ধান করতে সক্ষম হওয়া উচিত। কমপক্ষে, এটি এসকিউএলাইট 3.7.13 এর মতো দেখাচ্ছে।
কেন উইলিয়ামস

@ কেন উইলিয়ামস প্রিটি অনেকগুলি ডাটাবেসে `অফসেট``-তে একই সমস্যা রয়েছে `` এটি একটি ডাটাবেসকে জিজ্ঞাসা করার জন্য একটি অত্যন্ত অযোগ্য উপায় কারণ এটি পড়তে হবে যে অনেকগুলি সারি সবেমাত্র এটি ফিরে আসে 1
জোনাথন অ্যালেন

1
মনে রাখবেন যে আমি / স্থির আকার / রেকর্ডের বিষয়ে কথা বলছিলাম - ডেটাতে সঠিক বাইটে সরাসরি স্ক্যান করা সহজ হওয়া উচিত ( যেগুলি অনেকগুলি সারি পাঠ করে না ) তবে তাদের অপ্টিমাইজেশনটি স্পষ্টভাবে প্রয়োগ করতে হবে।
কেন উইলিয়ামস

@ কেনউইলিয়ামস: এসকিউএলাইটে স্থির আকারের রেকর্ড নেই, এটি গতিশীলভাবে টাইপ করা হয় এবং ডেটা ঘোষিত অ্যাফিলিটিগুলির সাথে মেলে না ( sqlite.org/fileformat2.html#section_2_1 )। সমস্ত কিছুই খ-গাছের পৃষ্ঠাগুলিতে সঞ্চিত রয়েছে, সুতরাং যে কোনও উপায়ে এটি পাতার দিকে কমপক্ষে একটি বি-ট্রি অনুসন্ধান করতে হবে। এটি দক্ষতার সাথে সম্পাদন করতে প্রতিটি সন্তানের পয়েন্টার সহ সাবট্রির আকার সংরক্ষণ করতে হবে। এটি সামান্য উপকারের জন্য খুব বেশি পরিমাণে ওভারহেড হবে, যেহেতু আপনি এখনও যোগ দেওয়ার জন্য, অফার ইত্যাদি দ্বারা
অফফেসটি

13
SELECT   bar
FROM     foo
ORDER BY Random()
LIMIT    1

11
যেহেতু এটি পুরো টেবিলের সামগ্রীটি প্রথমে নির্বাচন করবে, তাই এটি বড় টেবিলগুলির জন্য খুব সময়সাপেক্ষ হবে না?
অ্যালেক্স_কোডার

1
আপনি কেবল "WHERE" শর্ত (গুলি) ব্যবহার করে সুযোগটি সীমাবদ্ধ করতে পারবেন না?
jldupont

11

এখানে @ অঙ্কুর সমাধানের একটি পরিবর্তন রয়েছে:

SELECT * 
FROM table
LIMIT 1 
OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)

এই সমাধানটি ফাঁকগুলি সহ সূচকগুলির জন্যও কাজ করে, কারণ আমরা একটি পরিসরে [0, গণনা) একটি অফসেট এলোমেলো করে। MAXখালি টেবিল সহ কেস পরিচালনা করতে ব্যবহৃত হয়।

16k সারি সহ একটি টেবিলে এখানে সাধারণ পরীক্ষার ফলাফল রয়েছে:

sqlite> .timer on
sqlite> select count(*) from payment;
16049
Run Time: real 0.000 user 0.000140 sys 0.000117

sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
14746
Run Time: real 0.002 user 0.000899 sys 0.000132
sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
12486
Run Time: real 0.001 user 0.000952 sys 0.000103

sqlite> select payment_id from payment order by random() limit 1;
3134
Run Time: real 0.015 user 0.014022 sys 0.000309
sqlite> select payment_id from payment order by random() limit 1;
9407
Run Time: real 0.018 user 0.013757 sys 0.000208

4

আমি বড় স্ক্যালিটি 3 ডাটাবেসের জন্য নিম্নলিখিত সমাধানটি নিয়ে এসেছি :

SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1; 

অ্যাবস (এক্স) ফাংশনটি X টির সাথে সংখ্যার আর্গুমেন্টের পরম মান প্রদান করে

এলোমেলো () ফাংশনটি -9223372036854775808 এবং +9223372036854775807 এর মধ্যে একটি সিউডো-এলোমেলো পূর্ণসংখ্যা প্রদান করে।

অপারেটর% এর বাম অপারেন্ডের পূর্ণসংখ্যার মানটি তার ডান অপারেণ্ডের পরিবর্তিত করে।

অবশেষে, আপনি 0 এর সমান রোয়িড প্রতিরোধ করতে +1 যুক্ত করুন।


1
ভাল চেষ্টা করুন তবে আমি মনে করি না এটি কার্যকর হবে। তাহলে কি সারউইড = 5 সহ একটি সারি মুছে ফেলা হয়েছে, তবে সারিআইডিস 1,2,3,4,6,7,8,9,10 এখনও বিদ্যমান? তারপরে, যদি এলোমেলোভাবে সারি নির্বাচিত হয় 5 হয় তবে এই ক্যোয়ারীটি কিছুই ফিরিয়ে দেবে না।
ক্যালিকোডার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.