Ako rozhádzať DataTable
Nedávno som narazil na to, že potrebujem zobraziť nejaké dáta v náhodnom poradí pri každom zobrazení stránky.
MS SQL riešenie
Najprv som to chcel riešiť na úrovni MS SQL databázy.
MS SQL má funkciu RAND ktorá je ale dosť nepraktická a na tento účel nepoužiteľná. Napriek tomu existuje pomerne jednoduché riešenie:
SELECT * FROM table ORDER BY NEWID()
Funkcia NEWID vygeneruje náhodný identifikátor pre každý riadok. Takže vďaka ORDER BY klauzule budú výsledky vždy v inom poradí. Dá sa to použiť aj v kombinácii s TOP klauzulou, napr.:
SELECT TOP(10) * FROM table WHERE cid < 1000 ORDER BY NEWID()
Nevýhodou je, že NEWID() sa generuje vždy pre každý jeden riadok celej tabuľky. V každom prípade je toto riešenie veľmi rýchle.
.NET riešenie
Ďaľšou možnosťou je rozhádzať výsledky na úrovni aplikácie.
Nasledujúca statická metóda dostane na vstup parameter typu DataTable a vráti nový DataTable s náhodne poprehadzovanými riadkami:
public static DataTable Randomize(DataTable dt) { if (dt == null) throw new ArgumentNullException("dt"); dt.AcceptChanges(); //Vytvorím novú DataTable s rovnakou štruktúrou DataTable dtnew = dt.Clone(); //Vytvorím nový DataView nad tabuľkou dt DataView dv = dt.DefaultView; Random r = new Random(); int randomIndex = 0; while (dv.Count > 0) { //Vyberiem náhodný index (riadok) randomIndex = r.Next(0, dv.Count); //Pridám vybraný riadok do novej DataTable dtnew.Rows.Add(dv[randomIndex].Row.ItemArray); //Nastavím vybraný riadok zo starej DataTable na "vymazaný" dv.Delete(randomIndex); } //Zruším "vymazanie" riadkov dt.RejectChanges(); dv = null; return dtnew; }
Komentáre by mali byť jasné. Výhodou tohto prístupu je, že sa dá použiť viackrát nad už vytiahnutými dátami z DB. Taktiež je menej zaťažená databáza a dá sa použiť kedykoľvek ad-hoc.
Implementácia pre .NET 3.5
Ak používate .NET 3.5, môžete si túto metódu naimplementovať ako extension metódu nad DataTable:
public static class DataUtils { public static DataTable Randomize(this DataTable dt) { dt.AcceptChanges(); //Vytvorím novú DataTable s rovnakou štruktúrou DataTable dtnew = dt.Clone(); //Vytvorím nový DataView nad tabuľkou dt DataView dv = dt.DefaultView; Random r = new Random(); int randomIndex = 0; while (dv.Count > 0) { //Vyberiem náhodný index (riadok) randomIndex = r.Next(0, dv.Count); //Pridám vybraný riadok do novej DataTable dtnew.Rows.Add(dv[randomIndex].Row.ItemArray); //Nastavím vybraný riadok zo starej DataTable na "vymazaný" dv.Delete(randomIndex); } //Zruším "vymazanie" riadkov dt.RejectChanges(); dv = null; return dtnew; } }
Potom môžeme použiť nasledovný jednoduchý zápis:
DataTable random = dt.Randomize();
Ak máte iné skúsenosti s náhodným poradím dát, podeľte sa o ne v diskusii.
Update 6. 7. 2009
Dnes ma napadlo aj ďalšie riešenie, ako náhodne poprehadzovať riadky DataTable.
Najprv pridám nový stĺpec, dám doňho náhodné hodnoty a potom to podľa neho zotriedim. Na generovanie náhodných hodnôt použijem metódu Guid.NewGuid(), ktorá vždy vráti iný a náhodný identifikátor.
Tu je teda ďalšie riešenie:
public static class DataUtils { public static DataTable RandomizeEx(this DataTable dt) { if (dt.Rows.Count == 0) return null; //vytvorim unikatny nazov stlpca, ktory bude obsahovat "nahodne" retazce string randomColumn = string.Format("Random_{0}", Guid.NewGuid()); //pridam stlpec do tabulky dt.Columns.Add(randomColumn, typeof(string)); //pre kazdy riadok pridam nahodny retazec - Guid for (int i = 0; i < dt.Rows.Count; i++) { dt.Rows[i][randomColumn] = Guid.NewGuid().ToString(); } //dam zotriedit DataView podla stlpca s nahodnymi hodnotami dt.DefaultView.Sort = randomColumn; //vratim novu DataTable zo zotriedeneho DataView return dt.DefaultView.ToTable(); } }