在SQL上,可以使用order by NewID()的方式去做到選取隨機資料的功能,但是到了LINQ時似乎是沒那麼方便,保哥有介紹了一篇如何讓 LINQ 支援亂數排序功能,但是會需要在DB上建View跟Function,個人比較懶惰,所以想找找有沒有不動DB就可以做到的方式,後來試了半天還真的有辦法。
Solution 1
首先,來看這個看起來最簡潔的方式。
Solution 1
首先,來看這個看起來最簡潔的方式。
var Result = DB.Country_IPs.AsEnumerable().OrderBy(p => Guid.NewGuid()).Take(1);
這個效果等同於
select top 1 * from [dbo].[Country_IP] order by NewID()
我沒仔細研究內部是怎麼運作的,不過看起來是將資料都讀進程式後才作亂數排序,所以實際跑起來的速度甚至比select all的速度還慢上一點,在我的電腦上對約有11萬筆資料的table進行操作,約要800~900ms的時間,所以建議要使用這方法最好是對資料量小的資料表進行操作,不然會拖慢不少速度。
Solution 2
另一個方法則是要對Data Model做點手腳,在你的DataContext中,加上下面這段
[System.Data.Linq.Mapping.Function(Name = "NEWID", IsComposable = true)] public System.Guid Random() { throw new System.NotImplementedException(); }
如果不知道去那加的話,可以在Solution Explorer裡,把你的dbml點開,然後去裡面的cs加。
加完之後長的這個樣子
然後接下來的實用就很簡單了,只要orderby我們剛剛建的function就好。
var Result = (from p in DB.Country_IPs orderby DB.Random() select p).Take(1);
第二個方法的速度比第一個快很多,與使用SqlDataAdapter去跑的速度相比,只慢了大約100ms,個人覺得是相當不錯的選擇,可以參考看看。
the other test
by the way,我後來試了一下保哥的作法,原本想說從DB做應該會更快點,沒想到速度比第一種方式還慢,蠻出乎我意料的。
保哥看到別生氣啊XDDDDD
test code
最後附上落落長的測試code,歡迎大家提出指正與改進之處:)
LINQDemoDataContext DB = new LINQDemoDataContext(); string queryString = "select top 1 * from [dbo].[Country_IP] order by NewID()"; int start, end; DataSet dt = new DataSet(); DB.Log = Console.Out; Console.WriteLine("LINQ to SQL"); start = System.Environment.TickCount; //var Result = DB.Country_IPs.AsEnumerable().OrderBy(p => Guid.NewGuid()).Take(1); //var Result = (from p in DB.Country_IPs orderby DB.Random() select p).Take(1); var Result = (from p in DB.Country_IPs orderby DB.GetNewId() select p).Take(1); end = System.Environment.TickCount; foreach (var item in Result) { Console.WriteLine(item.IPStart + "\t" + item.IPEnd + "\t" + item.NumStart + "\t" + item.NumEnd + "\t" + item.Short + "\t" + item.Long); } Console.WriteLine("Run Time:" + (end - start)); Console.WriteLine(); Console.WriteLine("SqlDataAdapter"); Console.WriteLine(queryString); start = System.Environment.TickCount; using (SqlConnection connection = new SqlConnection(connectionString)) { SqlDataAdapter adapter = new SqlDataAdapter(); adapter.SelectCommand = new SqlCommand(queryString, connection); adapter.Fill(dt); } end = System.Environment.TickCount; for (int i = 0; i < dt.Tables.Count; i++) { Console.WriteLine("Table " + i.ToString() + " [" + dt.Tables[i].TableName + "]"); for (int j = 0; j < dt.Tables[i].Columns.Count; j++) { Console.Write(dt.Tables[i].Columns[j].ColumnName + "\t"); } Console.WriteLine(); for (int j = 0; j < dt.Tables[i].Rows.Count; j++) { for (int k = 0; k < dt.Tables[i].Columns.Count; k++) { Console.Write(dt.Tables[i].Rows[j][k].ToString() + "\t"); } Console.WriteLine(); } } Console.WriteLine("Run Time:" + (end - start)); Console.ReadLine();
No comments:
Post a Comment