[{"data":1,"prerenderedAt":769},["ShallowReactive",2],{"blog:2014:optimizing-sum-count-min-max-and-average-with-linq":3,"blogMore-Development":755,"comments-optimizing-sum-count-min-max-and-average-with-linq":768},{"id":4,"title":5,"body":6,"category":735,"commentCount":67,"date":736,"description":12,"excerpt":737,"extension":738,"filenames":739,"hidden":740,"image":739,"meta":741,"minutes":105,"navigation":742,"path":743,"seo":744,"showCategory":739,"stem":745,"tags":746,"updated":739,"url":752,"wordCount":753,"__hash__":754},"content\u002Fblog\u002F2014\u002Foptimizing-sum-count-min-max-and-average-with-linq.md","Optimizing Sum, Count, Min, Max and Average with LINQ",{"type":7,"value":8,"toc":729},"minimark",[9,13,16,21,134,137,141,144,278,281,326,329,332,336,339,444,447,481,485,488,597,600,720,725],[10,11,12],"p",{},"LINQ is a great tool for C# programmers letting you use familiar syntax with a variety of back-end systems without having to learn another language or paradigm for many query operations.",[10,14,15],{},"Ensuring that the queries still perform well can be a bit of a chore and one set that fails quite badly are the aggregate operations when you want more than one.",[17,18,20],"h2",{"id":19},"multiple-sequential-queries-bad","Multiple sequential queries (bad)",[22,23,28],"pre",{"className":24,"code":25,"language":26,"meta":27,"style":27},"language-csharp shiki shiki-themes everforest-light dracula","var count = db.Invoices.Count();\nvar total = db.Invoices.Sum(i => i.Paid);\nvar average = db.Invoices.Average(i => i.Paid);\n","csharp","",[29,30,31,65,103],"code",{"__ignoreMap":27},[32,33,36,40,44,48,51,55,58,62],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"sXAHl","var",[32,41,43],{"class":42},"s6Vpi"," count ",[32,45,47],{"class":46},"s9HRq","=",[32,49,50],{"class":42}," db.",[32,52,54],{"class":53},"sSKRk","Invoices",[32,56,57],{"class":42},".",[32,59,61],{"class":60},"sS4Kt","Count",[32,63,64],{"class":42},"();\n",[32,66,68,70,73,75,77,79,81,84,87,91,94,97,100],{"class":34,"line":67},2,[32,69,39],{"class":38},[32,71,72],{"class":42}," total ",[32,74,47],{"class":46},[32,76,50],{"class":42},[32,78,54],{"class":53},[32,80,57],{"class":42},[32,82,83],{"class":60},"Sum",[32,85,86],{"class":42},"(",[32,88,90],{"class":89},"s7cAX","i",[32,92,93],{"class":46}," =>",[32,95,96],{"class":42}," i.",[32,98,99],{"class":53},"Paid",[32,101,102],{"class":42},");\n",[32,104,106,108,111,113,115,117,119,122,124,126,128,130,132],{"class":34,"line":105},3,[32,107,39],{"class":38},[32,109,110],{"class":42}," average ",[32,112,47],{"class":46},[32,114,50],{"class":42},[32,116,54],{"class":53},[32,118,57],{"class":42},[32,120,121],{"class":60},"Average",[32,123,86],{"class":42},[32,125,90],{"class":89},[32,127,93],{"class":46},[32,129,96],{"class":42},[32,131,99],{"class":53},[32,133,102],{"class":42},[10,135,136],{},"Will issue three separate requests. There is nothing a LINQ provider can do to optimize that pattern as they are three discrete statements.",[17,138,140],{"id":139},"background","Background",[10,142,143],{},"If we wanted these values by country we could do this in LINQ:",[22,145,147],{"className":24,"code":146,"language":26,"meta":27,"style":27},"var a = db.Invoices.GroupBy(i => i.Country)\n      .Select(g => new { Country = g.Key,\n           Count = g.Count(),\n           Total = g.Sum(i => i.Paid),\n           Average = g.Average(i => i.Paid) });\n",[29,148,149,181,214,228,253],{"__ignoreMap":27},[32,150,151,153,156,158,160,162,164,167,169,171,173,175,178],{"class":34,"line":35},[32,152,39],{"class":38},[32,154,155],{"class":42}," a ",[32,157,47],{"class":46},[32,159,50],{"class":42},[32,161,54],{"class":53},[32,163,57],{"class":42},[32,165,166],{"class":60},"GroupBy",[32,168,86],{"class":42},[32,170,90],{"class":89},[32,172,93],{"class":46},[32,174,96],{"class":42},[32,176,177],{"class":53},"Country",[32,179,180],{"class":42},")\n",[32,182,183,186,189,191,194,196,200,203,205,208,211],{"class":34,"line":67},[32,184,185],{"class":42},"      .",[32,187,188],{"class":60},"Select",[32,190,86],{"class":42},[32,192,193],{"class":89},"g",[32,195,93],{"class":46},[32,197,199],{"class":198},"smiwp"," new",[32,201,202],{"class":42}," { Country ",[32,204,47],{"class":46},[32,206,207],{"class":42}," g.",[32,209,210],{"class":53},"Key",[32,212,213],{"class":42},",\n",[32,215,216,219,221,223,225],{"class":34,"line":105},[32,217,218],{"class":42},"           Count ",[32,220,47],{"class":46},[32,222,207],{"class":42},[32,224,61],{"class":60},[32,226,227],{"class":42},"(),\n",[32,229,231,234,236,238,240,242,244,246,248,250],{"class":34,"line":230},4,[32,232,233],{"class":42},"           Total ",[32,235,47],{"class":46},[32,237,207],{"class":42},[32,239,83],{"class":60},[32,241,86],{"class":42},[32,243,90],{"class":89},[32,245,93],{"class":46},[32,247,96],{"class":42},[32,249,99],{"class":53},[32,251,252],{"class":42},"),\n",[32,254,256,259,261,263,265,267,269,271,273,275],{"class":34,"line":255},5,[32,257,258],{"class":42},"           Average ",[32,260,47],{"class":46},[32,262,207],{"class":42},[32,264,121],{"class":60},[32,266,86],{"class":42},[32,268,90],{"class":89},[32,270,93],{"class":46},[32,272,96],{"class":42},[32,274,99],{"class":53},[32,276,277],{"class":42},") });\n",[10,279,280],{},"Which gets us everything in a single statement broken down by country. In SQL this is:",[22,282,286],{"className":283,"code":284,"language":285,"meta":27,"style":27},"language-sql shiki shiki-themes everforest-light dracula","SELECT Country, Count(*), Sum(Paid), Average(Paid)\n    FROM Invoices GROUP BY Country\n","sql",[29,287,288,312],{"__ignoreMap":27},[32,289,290,293,296,299,301,304,307,309],{"class":34,"line":35},[32,291,292],{"class":198},"SELECT",[32,294,295],{"class":42}," Country, ",[32,297,61],{"class":298},"saSZQ",[32,300,86],{"class":42},[32,302,303],{"class":46},"*",[32,305,306],{"class":42},"), ",[32,308,83],{"class":298},[32,310,311],{"class":42},"(Paid), Average(Paid)\n",[32,313,314,317,320,323],{"class":34,"line":67},[32,315,316],{"class":198},"    FROM",[32,318,319],{"class":42}," Invoices ",[32,321,322],{"class":198},"GROUP BY",[32,324,325],{"class":42}," Country\n",[10,327,328],{},"Many data sources including SQL are happy to provide aggregate values without a group by so how do we generate that from LINQ?",[10,330,331],{},"In the absence of a Group method that doesn’t take a property we need to fake it and because of the way many LINQ providers optimize out parts of the tree we can:",[17,333,335],{"id":334},"single-optimized-query-good","Single optimized query (good)",[10,337,338],{},"Replacing the property in a GroupBy with a constant value gives us an optimized single query:",[22,340,342],{"className":24,"code":341,"language":26,"meta":27,"style":27},"var a = db.Invoices.GroupBy(i => 1)\n    .Select(g => new { Count = g.Count(),\n               Total = g.Sum(i => i.Paid),\n               Average = g.Average(i => i.Paid) });\n",[29,343,344,372,398,421],{"__ignoreMap":27},[32,345,346,348,350,352,354,356,358,360,362,364,366,370],{"class":34,"line":35},[32,347,39],{"class":38},[32,349,155],{"class":42},[32,351,47],{"class":46},[32,353,50],{"class":42},[32,355,54],{"class":53},[32,357,57],{"class":42},[32,359,166],{"class":60},[32,361,86],{"class":42},[32,363,90],{"class":89},[32,365,93],{"class":46},[32,367,369],{"class":368},"s3Ipq"," 1",[32,371,180],{"class":42},[32,373,374,377,379,381,383,385,387,390,392,394,396],{"class":34,"line":67},[32,375,376],{"class":42},"    .",[32,378,188],{"class":60},[32,380,86],{"class":42},[32,382,193],{"class":89},[32,384,93],{"class":46},[32,386,199],{"class":198},[32,388,389],{"class":42}," { Count ",[32,391,47],{"class":46},[32,393,207],{"class":42},[32,395,61],{"class":60},[32,397,227],{"class":42},[32,399,400,403,405,407,409,411,413,415,417,419],{"class":34,"line":105},[32,401,402],{"class":42},"               Total ",[32,404,47],{"class":46},[32,406,207],{"class":42},[32,408,83],{"class":60},[32,410,86],{"class":42},[32,412,90],{"class":89},[32,414,93],{"class":46},[32,416,96],{"class":42},[32,418,99],{"class":53},[32,420,252],{"class":42},[32,422,423,426,428,430,432,434,436,438,440,442],{"class":34,"line":230},[32,424,425],{"class":42},"               Average ",[32,427,47],{"class":46},[32,429,207],{"class":42},[32,431,121],{"class":60},[32,433,86],{"class":42},[32,435,90],{"class":89},[32,437,93],{"class":46},[32,439,96],{"class":42},[32,441,99],{"class":53},[32,443,277],{"class":42},[10,445,446],{},"Here are the providers I’ve tried:",[448,449,450,458,469,475],"ul",{},[451,452,453,454],"li",{},"LINQ to Objects ",[455,456,457],"em",{},"(Works although constant is likely evaluated)",[451,459,460,461],{},"LINQ to SQL ",[455,462,463,464,468],{},"(Works although passes ",[465,466,467],"strong",{},"1"," parameter to SQL)",[451,470,471,472],{},"Entity Framework 6 ",[455,473,474],{},"(Works although query is a little obscure)",[451,476,477,478],{},"ElasticLINQ ",[455,479,480],{},"(Works and optimizes out totally)",[17,482,484],{"id":483},"countwhere-optimizations","Count+Where optimizations",[10,486,487],{},"If we are performing counts with a predicate or against a where we can also optimize these.",[22,489,491],{"className":24,"code":490,"language":26,"meta":27,"style":27},"var high = db.Invoices.Count(i => i.Paid >= 1000);\nvar low = db.Invoices.Where(i => i.Paid \u003C 1000).Count();\nvar sum = db.Invoices.Sum(i => i.Paid);\n",[29,492,493,528,568],{"__ignoreMap":27},[32,494,495,497,500,502,504,506,508,510,512,514,516,518,520,523,526],{"class":34,"line":35},[32,496,39],{"class":38},[32,498,499],{"class":42}," high ",[32,501,47],{"class":46},[32,503,50],{"class":42},[32,505,54],{"class":53},[32,507,57],{"class":42},[32,509,61],{"class":60},[32,511,86],{"class":42},[32,513,90],{"class":89},[32,515,93],{"class":46},[32,517,96],{"class":42},[32,519,99],{"class":53},[32,521,522],{"class":46}," >=",[32,524,525],{"class":368}," 1000",[32,527,102],{"class":42},[32,529,530,532,535,537,539,541,543,546,548,550,552,554,556,559,561,564,566],{"class":34,"line":67},[32,531,39],{"class":38},[32,533,534],{"class":42}," low ",[32,536,47],{"class":46},[32,538,50],{"class":42},[32,540,54],{"class":53},[32,542,57],{"class":42},[32,544,545],{"class":60},"Where",[32,547,86],{"class":42},[32,549,90],{"class":89},[32,551,93],{"class":46},[32,553,96],{"class":42},[32,555,99],{"class":53},[32,557,558],{"class":46}," \u003C",[32,560,525],{"class":368},[32,562,563],{"class":42},").",[32,565,61],{"class":60},[32,567,64],{"class":42},[32,569,570,572,575,577,579,581,583,585,587,589,591,593,595],{"class":34,"line":105},[32,571,39],{"class":38},[32,573,574],{"class":42}," sum ",[32,576,47],{"class":46},[32,578,50],{"class":42},[32,580,54],{"class":53},[32,582,57],{"class":42},[32,584,83],{"class":60},[32,586,86],{"class":42},[32,588,90],{"class":89},[32,590,93],{"class":46},[32,592,96],{"class":42},[32,594,99],{"class":53},[32,596,102],{"class":42},[10,598,599],{},"Then we can express this as:",[22,601,603],{"className":24,"code":602,"language":26,"meta":27,"style":27},"var a = db.Invoices.GroupBy(g => 1)\n    .Select(g => new { High = g.Count(i => i.Paid >= 1000),\n                   Low = g.Count(i => i.Paid \u003C 1000),\n                   Sum = g.Sum(i => i.Paid) });\n",[29,604,605,631,670,697],{"__ignoreMap":27},[32,606,607,609,611,613,615,617,619,621,623,625,627,629],{"class":34,"line":35},[32,608,39],{"class":38},[32,610,155],{"class":42},[32,612,47],{"class":46},[32,614,50],{"class":42},[32,616,54],{"class":53},[32,618,57],{"class":42},[32,620,166],{"class":60},[32,622,86],{"class":42},[32,624,193],{"class":89},[32,626,93],{"class":46},[32,628,369],{"class":368},[32,630,180],{"class":42},[32,632,633,635,637,639,641,643,645,648,650,652,654,656,658,660,662,664,666,668],{"class":34,"line":67},[32,634,376],{"class":42},[32,636,188],{"class":60},[32,638,86],{"class":42},[32,640,193],{"class":89},[32,642,93],{"class":46},[32,644,199],{"class":198},[32,646,647],{"class":42}," { High ",[32,649,47],{"class":46},[32,651,207],{"class":42},[32,653,61],{"class":60},[32,655,86],{"class":42},[32,657,90],{"class":89},[32,659,93],{"class":46},[32,661,96],{"class":42},[32,663,99],{"class":53},[32,665,522],{"class":46},[32,667,525],{"class":368},[32,669,252],{"class":42},[32,671,672,675,677,679,681,683,685,687,689,691,693,695],{"class":34,"line":105},[32,673,674],{"class":42},"                   Low ",[32,676,47],{"class":46},[32,678,207],{"class":42},[32,680,61],{"class":60},[32,682,86],{"class":42},[32,684,90],{"class":89},[32,686,93],{"class":46},[32,688,96],{"class":42},[32,690,99],{"class":53},[32,692,558],{"class":46},[32,694,525],{"class":368},[32,696,252],{"class":42},[32,698,699,702,704,706,708,710,712,714,716,718],{"class":34,"line":230},[32,700,701],{"class":42},"                   Sum ",[32,703,47],{"class":46},[32,705,207],{"class":42},[32,707,83],{"class":60},[32,709,86],{"class":42},[32,711,90],{"class":89},[32,713,93],{"class":46},[32,715,96],{"class":42},[32,717,99],{"class":53},[32,719,277],{"class":42},[10,721,722],{},[455,723,724],{},"[)amien",[726,727,728],"style",{},"html pre.shiki code .sXAHl, html code.shiki .sXAHl{--shiki-default:#3A94C5;--shiki-dark:#FF79C6}html pre.shiki code .s6Vpi, html code.shiki .s6Vpi{--shiki-default:#5C6A72;--shiki-dark:#F8F8F2}html pre.shiki code .s9HRq, html code.shiki .s9HRq{--shiki-default:#F57D26;--shiki-dark:#FF79C6}html pre.shiki code .sSKRk, html code.shiki .sSKRk{--shiki-default:#35A77C;--shiki-dark:#F8F8F2}html pre.shiki code .sS4Kt, html code.shiki .sS4Kt{--shiki-default:#8DA101;--shiki-dark:#50FA7B}html pre.shiki code .s7cAX, html code.shiki .s7cAX{--shiki-default:#5C6A72;--shiki-default-font-style:inherit;--shiki-dark:#FFB86C;--shiki-dark-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .smiwp, html code.shiki .smiwp{--shiki-default:#F85552;--shiki-dark:#FF79C6}html pre.shiki code .saSZQ, html code.shiki .saSZQ{--shiki-default:#DFA000;--shiki-dark:#8BE9FD}html pre.shiki code .s3Ipq, html code.shiki .s3Ipq{--shiki-default:#DF69BA;--shiki-dark:#BD93F9}",{"title":27,"searchDepth":67,"depth":67,"links":730},[731,732,733,734],{"id":19,"depth":67,"text":20},{"id":139,"depth":67,"text":140},{"id":334,"depth":67,"text":335},{"id":483,"depth":67,"text":484},"Development","2014-09-04T12:15:20+00:00","[object Object]","md",null,false,{},true,"\u002Fblog\u002F2014\u002Foptimizing-sum-count-min-max-and-average-with-linq",{"title":5,"description":12},"blog\u002F2014\u002Foptimizing-sum-count-min-max-and-average-with-linq",[747,748,749,750,751],".NET","Elasticsearch","LINQ","Entity Framework","C#","\u002Fblog\u002F2014\u002Foptimizing-sum-count-min-max-and-average-with-linq\u002F",594,"sCNxpai2VOc0dnLs1z8BEuG5mCXOc9beptZFss8MztE",[756,760,764],{"title":757,"date":758,"url":759},"Transactions in the MongoDB EF Core Provider","2025-10-25","\u002Fblog\u002F2025\u002Fmongodb-explicit-transactions\u002F",{"title":761,"date":762,"url":763},"Queryable Encryption with the MongoDB EF Core Provider","2025-09-22","\u002Fblog\u002F2025\u002Fmongodb-queryable-encryption\u002F",{"title":765,"date":766,"url":767},"Lazy Loading with EF Core Proxies","2025-04-02","\u002Fblog\u002F2025\u002Fef-proxies\u002F",[],1780900526696]