0

I have following query:

Banner banner = Database.Banners.Where(b => b.IsPublish.Value && 
         b.Category.Value == (int) CategoryBanner.Banner &&
         b.PeriodShowCountAlready < b.PeriodShowCount ||
         b.ShowNext < DateTime.Now ).Take(1).FirstOrDefault();

I retrieve only one record. Suppose that in the DB 3 records satisfy this query and LINQ will return always same record. I want that returned random record, how can I do this?

2

3 Answers 3

3

Depending on your database, this might work:

var banners = Database.Banners.Where(b => b.IsPublish.Value && 
         b.Category.Value == (int) CategoryBanner.Banner &&
         b.PeriodShowCountAlready < b.PeriodShowCount ||
         b.ShowNext < DateTime.Now );

Then just skip a random number of banners...

var skip = new Random().Next(banners.Count() - 1);
var banner = banners.Skip(skip).FirstOrDefault();

As Amar points out, this is approach is less than optimal since it causes two trips to the databank. From my own experience profiling, the round-trip to another server most often costs much-much more than executing a simple SQL statement.

So, what options are there?

  • Fetch the entire set, and select a random banner locally. This is not optimal because the set might be very large. If you know the set to be small (less than 1000 records without binary fields [like the banner image], or less than 10k all in all should be negligible)

  • Ask the database how many object there are first, and then ask the database for the n:th object. This is bad because it causes two trips.

  • Make the database do everything, which entrails leaving the comfort of your ORM and writing a stored procedure manually. To that end you utilize newid() [on sql server] as order by parameter and everything is fast and good, but you have to do this on the server and not in your C# app.

Sign up to request clarification or add additional context in comments.

1 Comment

This is interesting. Good one :)
2

Update: The (far better) answer in the duplicated question provides an example with Skip. The benefit there is the empty list handling. Plus, this answer discusses issues with large result sets which won't be handled well in my solution...

You could return more than one from the db and select a random entry from the result.

List<Banner> list = Database.Banners.Where(b => b.IsPublish.Value && 
         b.Category.Value == (int) CategoryBanner.Banner &&
         b.PeriodShowCountAlready < b.PeriodShowCount ||
         b.ShowNext < DateTime.Now ).Take(10).ToList();

Random r = new Random();
Banner banner = list.Count == 0 ? null : list[r.Next(0, list.Count)];

1 Comment

But supposing that instead of taking a random row from the first 10 results, OP wants one from a set of 1 million results? That's pulling quite a lot of unnecessary data.
0

Get all the records and do random on them

Try doing this:

 var records = Database.Banners.Where(b => b.IsPublish.Value && 
 b.Category.Value == (int) CategoryBanner.Banner &&
 b.PeriodShowCountAlready < b.PeriodShowCount ||
 b.ShowNext < DateTime.Now ).ToList();
 var random = new Random();
 var count = random.Next(records.Count - 1);
 Banner banner = records[count];

7 Comments

"the range of return values ordinarily includes zero but not maxValue",so using random.Next(records.Count - 1) will never create an index for the last item. Also, does IQueryable support index operators? I don't think so.
There's actually nothing wrong with this example, save for the -1 and the fact that you may not want to fetch all the banners if you only need one. @spender - note that the records were ToList()-ed.
Forgot to do ToList on the result. IQueryable supports Count() though
@Gleno I forgot to put the ToList earlier, edited my post. My bad.
@Gleno: ToList wasn't present at the time of the comment. Even with ToList, in the case of a large result set, this would be somewhat sub-optimal.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.