7

I try to get random record from database:

 personToCall = db.Persons.Skip(toSkip).Take(1).First();

but I get exception which tells me:

{"The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'."}

Can I do it without OrderBy? Sorting data structure (O(nlogn)) to pick random element(which should be constant) doesn't look wise.

EDIT: I use Entity Framework 6.1.1.

4
  • Do you use Identity integer primary keys? Commented Sep 14, 2014 at 16:06
  • Both the answers below should work for you . Commented Sep 14, 2014 at 16:07
  • @Lukazoid Didn't hear about them. The model looks like public class Person { public int Id { get; set; } etc....} and Entity Framework created table basing on this code. Commented Sep 14, 2014 at 16:09
  • Even if you do I don't see how that would help. If they were guaranteed to be sequential and without gaps you could find the max and min with an efficient index operation then select a random number in that range with a seek. But they aren't guaranteed to be. Commented Sep 14, 2014 at 16:12

4 Answers 4

33

You can have something like :

personToCall = db.Persons.OrderBy(r => Guid.NewGuid()).Skip(toSkip).Take(1).First();

You should use FirstOrDefault to be mode defensive.

Here the dark lord teaching the force to yoda! what is the world coming to!

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

7 Comments

Unfortunately, this is dependent on the version of Entity Framework that the OP is using.
@JustinNiessner I use EF 6.1.1.
DarthVader will this LINQ query make any changes to my table?
Haha, the last line is just too funny
this is a good solution if you have ~1000 records or don't care about your CPU and time
|
6

First you need to get the random number from 1 to max record, see this

Random rand = new Random();
int toSkip = rand.Next(0, db.Persons.Count());

db.Persons.Skip(toSkip).Take(1).First();

with order by you can use the Guid.NewGuid()

db.Persons.OrderBy(x=>x.Guid.NewGuid()).Skip(toSkip).Take(1).FirstOrDefault();

3 Comments

This is simple and good, but it should be rand.Next(0, ...) or the first record will never be included
However, I noticed now that this becomes slow when the number of records increases, I now have approx 30,000 and it's too slow so I have to give up on this method
A set of parenthesis are needed after Count on the second line of the example code.
5

There is no way to do this without an ordering clause.

personToCall = db.Persons.OrderBy(r => Random.Next()).First();

That could be slow depending on the size of your Persons table, so if you wanted to make this fast, you'd have to add a column to Person, or join it to a dictionary of random numbers and Person keys, then order by that. But that is rarely a wise solution.

Better to ask a higher level question about the overall task at hand.

Comments

-1

To avoid the OrderBy, dump to a List and random pick against the Index:

VB

With New List(Of Persons)
    .AddRange(db.Persons)
    PersonToCall = .Item(New Random().Next(0, .Count - 1))
End With

C#

var people = new List<Persons>();
people.AddRange(db.Persons);
personToCall  = people.Item(new Random().Next(0, people.Count - 1));

1 Comment

If fetching the whole table doesn't scale for your purposes, another thought might be to have a View in your model on the database side: SELECT TOP 1 * FROM table ORDER BY NEWID()

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.