I am very new to writing LINQ statements, and I am struggling to write this simple SQL in LINQ form.
SELECT Type, COUNT(*) as [Total Submissions]
FROM Submission
GROUP BY Type
this is my attempt:
var query = from s in db.Submission
group s.Type
Type = s.Type
Total = s.Type.Count()
This is what my output should be:
Type Count of Type
Book 10
Chapter 15
Journal 8
Conference 4
Using LINQ syntax:
var result = from x in db.Submission
group x by x.Type into grp
select new {
Type = grp.Key,
Count = grp.Count()
};
Using lambda syntax:
var result = db.Submission
.GroupBy(x => x.Type)
.Select(x => new {
Type = x.Key,
Count = x.Count()
});
Related
SELECT StepID, count() as nb FROM Question GROUP BY StepID ORDER by nb;
You should probably go through the basics of LINQ. Microsoft Docs has a whole section dedicated to LINQ: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/
If you have your data in a List called as questions of type, List<Question> then you should be able to convert your Query like this:
var ret = from q in questions
group q by q.StepId into grouped
let count = grouped.Count()
orderby count
select new { StepId = grouped.Key, nb = count };
Query comprehension syntax:
from q in questions
group q by q.StepId into g
select new { StepId = g.Key, Count = g.Count() } into stepCount
orderby stepCount.Count
select stepCount;
Exact same in method syntax (which I prefer, since it can all query syntax can plus more and also often is more compact):
questions
.GroupBy(q => q.StepId)
.Select(g => new { StepId = g.Key, Count = g.Count() })
.OrderBy(stepCount => stepCount.Count)
Variant using another GroupBy overload:
questions
.GroupBy(q => q.StepId, (key, values) => new { StepId = key, Count = values.Count() })
.OrderBy(stepCount => stepCount.Count);
Given a list IQueryables, how can you sum the count of each, without having multiple statements executed in the database?
return queries
.Sum(qy=> qy.Count());
The above works, but hits the database for each query.
You can first use the Aggregate function with Concat to combine the IQueryable's and then Count the total like this:
return queries.Aggregate((x,y) => x.Concat(y)).Count()
Starting from this idea Sum(q1,q2) = q1.Concat(q2).Count() I've tested the following extensions:
public static class LinqExtensions
{
public static IQueryable<object> ConcatAny<T,R>(this IQueryable<T> q1, IQueryable<R> q2)
{
return q1.Select(c=>(object)null).Concat(q2.Select(c=>(object)null));
}
public static IQueryable<object> ConcatAll(this IEnumerable<IQueryable<object>> queries)
{
var resultQuery = queries.First();
foreach (var query in queries.Skip(1))
{
resultQuery = resultQuery.ConcatAny(query);
}
return resultQuery;
}
}
I assumed you have heterogeneous queries like IQueryable<T>, IQueryable<R> so on and you are interested in counting all rows no matter which the source is.
So you might use these extensions like
var q1 = Table1.AsQueryable();
var q2 = Table2.AsQueryable();
var q3 = Table3.AsQueryable();
var queries = new IQueryable<object>[] {q1,q2,q3}; // we use here the covariance feature
return queries.ConcatAll().Count();
The generated SQL might look like this
SELECT COUNT(*) AS [value]
FROM (
SELECT NULL AS [EMPTY]
FROM (
SELECT NULL AS [EMPTY]
FROM [Table1] AS [t0]
UNION ALL
SELECT NULL AS [EMPTY]
FROM [Table2] AS [t1]
) AS [t2]
UNION ALL
SELECT NULL AS [EMPTY]
FROM [Table3] AS [t3]
) AS [t4]
I don't think is very effective though
Ok, a few minutes late, but I got it!
Here is the code:
public static class LinqExtensions
{
public static int CountAll(this IEnumerable<IQueryable<object>> queries)
{
if (queries == null || !queries.Any())
{
throw new ArgumentException("Queries parameter cannot be null or empty");
}
Expression ex = Expression.Constant(0);
foreach (var qy in queries)
{
// create count expression
var expression = Expression.Call(
typeof(Queryable),
"Count",
new[] { qy.ElementType },
qy.Expression
);
ex = Expression.Add(ex, expression);
}
return queries.First().Provider.Execute<int>(ex);
}
}
You use it as queries.CountAll() where queries is an IEnumerable<IQueryable<object>> as in Adrian's answer or even simple IEnumerable<IQueryable>.
Here is a sample SQL result from the profiler:
exec sp_executesql N'SELECT #p0 + ((
SELECT COUNT(*)
FROM [A] AS [t0]
WHERE [t0].[i1] >= #p1
)) + ((
SELECT COUNT(*)
FROM [B] AS [t1]
WHERE [t1].[i2] >= #p2
)) + ((
SELECT COUNT(*)
FROM [C] AS [t2]
WHERE [t2].[i3] >= #p3
)) AS [value]',N'#p0 int,#p1 int,#p2 int,#p3 int',#p0=0,#p1=2,#p2=2,#p3=2
Which is the representation of
var a = db.GetTable<A>();
var b = db.GetTable<B>();
var c = db.GetTable<C>();
var q1 = a.Where(v => v.i1 >= 2);
var q2 = b.Where(v => v.i2 >= 2);
var q3 = c.Where(v => v.i3 >= 2);
var queries = new IQueryable<object>[] {
q1,q2,q3
};
Note that A, B and C are different objects/tables with different numbers of properties/columns and that the expressions are random Where filters.
If you are using Entity Framework you can use an extension called EntityFramework.Extended. There is a built in extension called Future Queries. This will allow you to specify that a query should be executed the next time a trip to the database is made.
NuGet command:
Install-Package EntityFramework.Extended
Sample code:
static void Main(string[] args)
{
using (var context = new MyDbContext())
{
var modelSet1 = context.Models.Where(x => x.ModelId < 25).FutureCount();
var modelSet2 = context.Models.Where(x => x.ModelId > 25 && x.ModelId < 32).FutureCount();
var modelSet3 = context.Models.Where(x => x.ModelId > 32).FutureCount();
var queries = new [] {modelSet1, modelSet2, modelSet3};
var countQueries = queries.Sum(x => x.Value);
Console.WriteLine(countQueries);
}
Console.ReadLine();
}
How can I get linq pad to run my left join as show below?
var query =
from s in db.CDBLogsHeaders
.OrderByDescending(g => g.LogDateTime)
from sc in db.StyleColors
.Where(stylecolor => stylecolor.id == (int?)s.StyleColorID)
.DefaultIfEmpty()
from c in db.StyleHeaders
.Where(styleHeader => styleHeader.id == (int?)s.StyleHeaderID)
.DefaultIfEmpty()
select new
{
CDBLogsHeaderId = s.Id,
Merchandiser = c.Merchandiser,
Material = s.Material,
Season = s.Season,
LogsHeaderLogType = s.LogType,
PushFromTo = s.PushFromTo,
LinePlan = s.LinePlan,
QuikRefNumber = s.QuikRefNumber,
PLMOrigin = s.PLM_Origin,
SeasonOriginal = c.SeasonOriginal,
SeasonCurrent = c.SeasonCurrent,
StyleHeaderId = c.Id,
StyleCode = c.StyleCode,
StyleColorsColorCode = sc.ColorCode
};
query.Dump();
The sql that linq pad creates runs perfectly in Management Studio but linq-pad doesn't display any rows and gives this error
InvalidOperationException: The null value cannot be assigned to a
member with type System.Int32 which is a non-nullable value type.
How can I get linqpad to work so I can play with it?
In your anonymous type, make sure your ints are returning ints.
Change
StyleHeaderId = c.Id,
To
StyleHeaderId = (c.Id == null ? 0 : c.Id),
I would like to translate the following SQL into LINQ:
SELECT
(Select count(BidID)) as TotalBidNum,
(Select sum(Amount)) as TotalBidVal
FROM Bids
I've tried this:
from b in _dataContext.Bids
select new { TotalBidVal = b.Sum(p => p.Amount), TotalBidNum = b.Count(p => p.BidId) }
but get an error "Bids does not contain a definition for "Sum" and no extension method "Sum" accepting a first argument of type "Bids" could be found.
How can I do this in LINQ?
Thanks
CONCLUDING:
The final answer was:
var ctx = _dataContext.Bids;
var itemsBid = (from b in _dataContext.Bids
select new { TotalBidVal = ctx.Sum(p => p.Amount), TotalBidNum = ctx.Count() }).First();
You can write this query using GroupBy. The Lambda expression is as follows:
var itemsBid = db.Bids
.GroupBy( i => 1)
.Select( g => new
{
TotalBidVal = g.Sum(item => item.Amount),
TotalBidNum = g.Count(item => item.BidId)
});
You could try this out. The variable b is an entity (for every iteration) while ctx is an entityset which has the extension methods you need.
var ctx = _dataContext.Bids;
var result = ctx
.Select( x => new
{
TotalBidVal = ctx.Sum ( p => p.Amount ),
TotalBidNum = ctx.Count( p => p.BidId )
} )
.First();
here's an alternative to scartag's solution:
(from b in _dataContext.Bids.Take(1)
select new
{
TotalBidVal = _dataContext.Bids.Sum(p => p.Amount),
TotalBidNum = _dataContext.Bids.Count()
}).Single();
Although there's no real reason you can't just say:
var result = new
{
TotalBidVal = _dataContext.Bids.Sum(p => p.Amount),
TotalBidNum = _dataContext.Bids.Count()
};
It hits the database twice, but its very readable
You could do it using the Aggregate Clause.
Aggregate t In _dataContext.Bids
Into TotalBidNum = Count(BidID),
TotalBidVal = Sum(Amount)
If you're using Fx4+ or an extension dll for Fx2, you could also benfit from parallelism by using
Aggregate t In _dataContext.Bids.AsParallel
I have SQL database as follows
alt text http://img97.imageshack.us/img97/5774/dbimage.jpg
Now I want to filter the restaurant_detail table for the parameters:
1. cuisine 2. area
Can you help me to build LINQ query?
I presume you have a model generated either with LINQ to SQL or Entity Framework. Also, I'm assuming foreign key relationships have been set.
var details = db
.Cuisines
.Where(c => c.Cuisine=="something")
.SelectMany(c => c.RestaurantCuisines)
.Select(rc => rc.Restaurant.RestaurantDetails)
.Where(rd => rd.Area=="something")
;
Done with the linq query using following lines of code :
c = from q in dc.restaurant_cuisines
where q.cuisine.cuisine1.Contains(cuisine)
&& q.restaurant.price.ToString().Length == price.Length
select new NearBy { NearById = q.restaurant.id, NearByLongitude = (double)q.restaurant.longitude, NearByLatitude = (double)q.restaurant.latitude };
}
int[] ids = new int[c.Count()];
var lon = from q1 in dc.area_maps where q1.area.ToLower() == area.ToLower() select q1.longtitude;
var lat = from q1 in dc.area_maps where q1.area.ToLower() == area.ToLower() select q1.latitude;
foreach(NearBy n in c)
{
result = calcDistNew((double)lat.FirstOrDefault(), (double)lon.FirstOrDefault(), n.NearByLatitude, n.NearByLongitude);
ids[i++] = n.NearById;
}
var r = from q in dc.restaurant_details
where 1 == 1 &&
(ids).Contains(q.restaurant_id)
select new Restaurant
{
Restora_id = q.restaurant_id.ToString(),
Name = q.restaurant.name,
Foodtype = q.restaurant.foodtype.foodtype1,
Avg_rating = q.restaurant.avg_rating.ToString(),
Featured = q.restaurant.featured.ToString(),
CuisineList = getCuisine(q.restaurant_id),
Restora_type = q.type,
Distance = Math.Round(calcDistNew((double)lat.FirstOrDefault(), (double)lon.FirstOrDefault(), (double)q.restaurant.latitude, (double)q.restaurant.longitude), 2),
Newarrival = q.restaurant.newarrival.ToString(),
CountRecord = ids.Length.ToString()
};
var d = r.AsEnumerable().OrderBy(t => t.Distance);
var g = d.Take(recordSize + 10).Skip(recordSize);
return g.ToList();
Please note that above displayed code generated with some changes from the initial requirements.