Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most optimized use of multiple Where statements

Is the following request automatically "optimized" ?

var result = initial
                .Where(Predicate1)
                .Where(Predicate2)
                .Where(Predicate3);

This is equivalent to

var result = initial
                .Where(e => Predicate1(e) && Predicate2(e) && Predicate3(e));

Which one of these statements is the more optimized of the two? Or are they identical?

like image 823
Toto Avatar asked Aug 06 '13 13:08

Toto


People also ask

Does more WHERE clause improve performance?

A where clause will generally increase the performance of the database. Generally, it is more expensive to return data and filter in the application. The database can optimize the query, using indexes and partitions. The database may be running in parallel, executing the query in parallel.

Is used to add in multiple conditions WHERE all conditions must be satisfied?

The OR operator is used to combine multiple conditions in an SQL statement's WHERE clause.

Can you have multiple WHERE statements in SQL?

You can specify multiple conditions in a single WHERE clause to, say, retrieve rows based on the values in multiple columns. You can use the AND and OR operators to combine two or more conditions into a compound condition.

Does order of WHERE clause affect performance?

The order in which columns appear in the WHERE clause does not affect query efficiency. Only the order of columns when defining the index matters.


2 Answers

Although the compiled code does not combine the predicates, the execution essentially does. Linq's Where method returns a WhereListIterator when passed a List. The WhereListIterator has its own Where method implementation that returns a new WhereListIterator with the predicates combined. It looks like this:

return new Enumerable.WhereListIterator<T>(this.source, Enumerable.CombinePredicates<T>(this.predicate, predicate)

where this.source is the List, this.predicate is the predicate of the first Where, and predicate is from the second Where.

The CombinePredicates returns a delegate containing the following code:

if (predicate1(source)) return predicate2(source);
return false;

So the chained Where clauses should end up with something like:

if (predicate1(source)) {
    if (predicate2(source) {
        return predicate3(source) {
    } 
    return false;
 }
 return false;

With a small list, using && to combine the predicates in a single Where is likely more efficient, but as the list size increases the runtime of the two options may become similar. You would have to profile it to quantify just what the difference is. I suspect it's not large enough to matter much.

like image 157
hatchet - done with SOverflow Avatar answered Nov 08 '22 17:11

hatchet - done with SOverflow


No, it's not. These Where methods calls are not combined into one.

c# code:

var input = new List<string>();

var output = input.Where(x => x.StartsWith("test")).Where(x => x.Length > 10).Where(x => !x.EndsWith("test"));

IL generated:

IL_0000: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldsfld class [mscorlib]System.Func`2<string, bool> ConsoleApplication2.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
IL_000c: brtrue.s IL_001f

IL_000e: ldnull
IL_000f: ldftn bool ConsoleApplication2.Program::'<Main>b__0'(string)
IL_0015: newobj instance void class [mscorlib]System.Func`2<string, bool>::.ctor(object, native int)
IL_001a: stsfld class [mscorlib]System.Func`2<string, bool> ConsoleApplication2.Program::'CS$<>9__CachedAnonymousMethodDelegate3'

IL_001f: ldsfld class [mscorlib]System.Func`2<string, bool> ConsoleApplication2.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
IL_0024: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, bool>)
IL_0029: ldsfld class [mscorlib]System.Func`2<string, bool> ConsoleApplication2.Program::'CS$<>9__CachedAnonymousMethodDelegate4'
IL_002e: brtrue.s IL_0041

IL_0030: ldnull
IL_0031: ldftn bool ConsoleApplication2.Program::'<Main>b__1'(string)
IL_0037: newobj instance void class [mscorlib]System.Func`2<string, bool>::.ctor(object, native int)
IL_003c: stsfld class [mscorlib]System.Func`2<string, bool> ConsoleApplication2.Program::'CS$<>9__CachedAnonymousMethodDelegate4'

IL_0041: ldsfld class [mscorlib]System.Func`2<string, bool> ConsoleApplication2.Program::'CS$<>9__CachedAnonymousMethodDelegate4'
IL_0046: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, bool>)
IL_004b: ldsfld class [mscorlib]System.Func`2<string, bool> ConsoleApplication2.Program::'CS$<>9__CachedAnonymousMethodDelegate5'
IL_0050: brtrue.s IL_0063

IL_0052: ldnull
IL_0053: ldftn bool ConsoleApplication2.Program::'<Main>b__2'(string)
IL_0059: newobj instance void class [mscorlib]System.Func`2<string, bool>::.ctor(object, native int)
IL_005e: stsfld class [mscorlib]System.Func`2<string, bool> ConsoleApplication2.Program::'CS$<>9__CachedAnonymousMethodDelegate5'

IL_0063: ldsfld class [mscorlib]System.Func`2<string, bool> ConsoleApplication2.Program::'CS$<>9__CachedAnonymousMethodDelegate5'
IL_0068: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, bool>)
IL_006d: pop
IL_006e: ret

As you can see, there are 3 System.Linq.Enumerable::Where<string> calls.

like image 29
MarcinJuraszek Avatar answered Nov 08 '22 16:11

MarcinJuraszek