I am trying to write a LINQ query, having multiple group by and scalar valued functions using Entity Framework.
This is a sample query with simpler names:
var test = context.<db_view>.Where(predicate)
.GroupBy(x => new {x.col1, x.col2, x.col3})
.Select(y => new
{
a = y.key.col1,
b = y.key.col2,
c = y.key.col3,
d = ctx.ScalarFunction(y.key.col2)
});
I however get an error:
"Column Distinct1.col1 is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
I do have col1 in the GROUP BY Clause. Am I missing something here?
This is the SQL Query generated by Entity Framework:
SELECT
1 AS [C1],
[Distinct1].[col1] AS [col1],
[Distinct1].[col2] AS [col2],
[Distinct1].[col3] AS [col3],
[dbo].[scalarfunction]([Distinct1].[col2]) AS [C2],
FROM ( SELECT DISTINCT
[Extent1].[col1] AS [col1],
[Extent1].[col2] AS [col2],
[Extent1].[col3] AS [col3],
FROM (SELECT
[view].[col1] AS [col1],
[view].[col2] AS [col2],
[view].[col3] AS [col3],
[view].[col4] AS [col4],
[view].[col5] AS [col5],
[view].[col6] AS [col6]
FROM [dbo].[view] AS [view]) AS [Extent1]
WHERE (predicate
) AS [Distinct1]
Why use GroupBy? You don't need groups. You just need (col1, col2 col3) distinct tuples.
So use the Distinct operator.
Try this:
var test = context.<db_view>.Where(predicate)
.Select(x => new {x.col1, x.col2, x.col3})
.Distinct()
.Select(y => new
{
a = y.col1,
b = y.col2,
c = y.col3,
d = ctx.ScalarFunction(y.col2)
});
Related
I have a table function which returns table names and number of entries within that table :
CREATE FUNCTION [dbo].[ufnGetLookups] ()
RETURNS
#lookupsWithItemCounts TABLE
(
[Name] VARCHAR(100),
[EntryCount] INT
)
AS
BEGIN
INSERT INTO #lookupsWithItemCounts([Name],[EntryCount])
VALUES
('Table1', (SELECT COUNT(*) FROM Table1)),
('Table2', (SELECT COUNT(*) FROM Table2)),
('Table3', (SELECT COUNT(*) FROM Table))
RETURN;
END
What would be the Linq equivalent of above simple function? Notice that I want to get the result in one single shot and the speed of the operation is quite important for me. If I realise that the converted linq to sql results in a massive bulky sql with performance hit, I would rather stick to my existing user defined function and forget about the linq equivilant.
You can do that with a UNION query. EG
var q = db.Books.GroupBy(g => "Books").Select(g => new { Name = g.Key, EntryCount = g.Count() })
.Union(db.Authors.GroupBy(g => "Authors").Select(g => new { Name = g.Key, EntryCount = g.Count() }));
var r = q.ToList();
Not an EF guy, and not sure if this would be more performant.
Select TableName = o.name
,RowCnt = sum(p.Rows)
From sys.objects as o
Join sys.partitions as p on o.object_id = p.object_id
Where o.type = 'U'
and o.is_ms_shipped = 0x0
and index_id < 2 -- 0:Heap, 1:Clustered
--and o.name in ('Table1','Table2','Table3' ) -- Include (or not) your own filter
Group By o.schema_id,o.name
Note: Wish I could recall the source of this, but I've used it in my discovery process.
I'm trying to generate following report from popular NorthWind DB using Linq. It should be group by Customer, OrderYear.
CustomerName OrderYear Amount
I've to use the following tables Customer,Order and Order Details.
So far this is what I've done.
NorthwindDataContext north = new NorthwindDataContext();
var query = from o in north.Orders
group o by o.Customer.CompanyName into cg
select new
{
Company = cg.Key,
YearGroup = ( from y in cg
group y by y.OrderDate.Value.Year into yg
select new
{
Year = yg.Key,
YearOrdes = yg
}
)
};
foreach (var q in query)
{
Console.WriteLine("Customer Name : " + q.Company);
foreach (var o in q.YearGroup)
{
Console.WriteLine("Year " + o.Year);
Console.WriteLine("Sum " + o.YearOrdes.Sum(yo => yo.Order_Details.Sum( yd=> Convert.ToDecimal(yd.UnitPrice* yd.Quantity))));
}
Console.WriteLine();
}
It is giving me expected results. I compared by running t-sql in back end.But, I've 2 questions.
In the Inner foreach, the 2nd statement generate the sum. Is it proper approach? Or there is better one available?
How to get the Sum in the Linq query itself.
Got it in single LINQ to SQL query:
var query = from o in north.Orders
from c in north.Customers.Where(c => c.CustomerID == o.CustomerID).DefaultIfEmpty()
from d in north.Order_Details.Where(d => d.OrderID == o.OrderID).DefaultIfEmpty()
group new { o, c, d } by new { o.OrderDate.Value.Year, c.CompanyName } into g
select new
{
Company = g.Key.CompanyName,
OrderYear = g.Key.Year,
Amount = g.Sum(e => e.d.UnitPrice * e.d.Quantity)
};
You can then simply get results:
var results = query.ToList();
Or sort it before fetching:
var results = query.OrderBy(g => g.Company).ThenByDescending(g => g.OrderYear).ToList();
I was curious about SQL that is generated by that LINQ to SQL query, so set custom Log and here it is:
SELECT [t5].[value22] AS [Company], [t5].[value2] AS [OrderYear], [t5].[value] AS [Amount]
FROM (
SELECT SUM([t4].[value]) AS [value], [t4].[value2], [t4].[value22]
FROM (
SELECT [t3].[UnitPrice] * (CONVERT(Decimal(29,4),[t3].[Quantity])) AS [value], [t3].[value] AS [value2], [t3].[value2] AS [value22]
FROM (
SELECT DATEPART(Year, [t0].[OrderDate]) AS [value], [t1].[CompanyName] AS [value2], [t2].[UnitPrice], [t2].[Quantity]
FROM [dbo].[Orders] AS [t0]
LEFT OUTER JOIN [dbo].[Customers] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID]
LEFT OUTER JOIN [dbo].[Order Details] AS [t2] ON [t2].[OrderID] = [t0].[OrderID]
) AS [t3]
) AS [t4]
GROUP BY [t4].[value2], [t4].[value22]
) AS [t5]
ORDER BY [t5].[value22], [t5].[value2] DESC
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.6387
A bit scary, isn't it? But if you look closer, there is standard LEFT JOIN used to combine all three tables together! All the rest is just grouping, sorting and summing.
var result = table1.Join(table2, o => o.ProgramID, t => t.ProgramID, (o, t) => new { o.ProgramID, t.Program })
.OrderBy(t => t.Program)
.Distinct();
the above linq statement actually returns the correct result, but he sql generated (below) is not as simple as it could be
SELECT [t2].[ProgramID], [t2].[Program]
FROM (
SELECT DISTINCT [t0].[ProgramID], [t1].[Program]
FROM [table1] AS [t0]
INNER JOIN [table2] AS [t1] ON [t0].[ProgramID] = [t1].[ProgramID]
) AS [t2]
ORDER BY [t2].[Program]
I would have thought the sql below is far cleaner but I'm not sure of the linq statement to achieve it.
select distinct
o.ProgramID,
t.Program
from
table1 0
inner join table2 t on t.ProgramID = o.ProgramID
order by t.Program
Thanks in advance
I don't know if it will help, but you can try something like this;
var result = (from o in table1
join t in table2 on o.ProgramID equals t.ProgramID
orderby t.Program
select new { o.ProgramID, t.Program }).Distinct();
I tried this and that works:
var result = (from o in table1
join t in table2 on o.ProgramID equals t.ProgramID
select new { o.ProgramID, t.Program })
.Distinct().OrderBy(t => t.Program)
.ThenBy(t => t.ProgramName)
.ThenBy(t => t.Description);
First you do Distinct and then OrderBy, then OrderBy works.
Profile the two queries, comparing stats-IO and the actual execution plan. It is entirely possible that it makes zero difference to the SQL server.
If you really want known TSQL, use ExecuteQuery-of-T and pass in the TSQL yourself. Maybe include some lock hints too (most commonly: NOLOCK)
We have the following test model in the dbml file:
Model http://www.freeimagehosting.net/uploads/a86582498a.gif
For the test case there are 4 records in the table, 1 parent, 3 children. We are looking for the siblings of a specific record, including the specific record.
using (var db = new TestDataContext())
{
var query =
from f in db.Foos
where f.Name == "Two"
select f.Foo1.Foos; // get the record's parent's children
var foos = query.SelectMany(f => f); // project the EntitySet
Assert.AreEqual(3, foos.Count()); // passes
}
This returns the correct items with the following SQL:
SELECT [t2].[FooId],
[t2].[ParentFooId],
[t2].[Name]
FROM [dbo].[Foos] AS [t0]
INNER JOIN [dbo].[Foos] AS [t1] ON [t1].[FooId] = [t0].[ParentFooId]
CROSS JOIN [dbo].[Foos] AS [t2]
WHERE ([t0].[Name] = #p0)
AND ([t2].[ParentFooId] = [t1].[FooId])
We are wondering about the CROSS JOIN, this apparently is the result of the SelectMany?
Is there another way we should approach this in order to not have the CROSS JOIN?
You can stack from statements in a Linq query and that will probably help you out here.
var query = from f in db.Foos
from f2 in f.Foos
where f.Name == "Two"
select f2;
Which produces.
SELECT [t1].[FooId],
[t1].[Name],
[t1].[ParentFooId]
FROM [dbo].[Foos] AS [t0], [dbo].[Foos] AS [t1]
WHERE ([t0].[Name] = #p0) AND ([t1].[ParentFooId] = [t0].[FooId])
You could alternatively do:
var query = from f in db.Foos
where (from fo in db.Foos
where fo.Name == "Two"
select fo.ParentId).Contains(f.ParentId)
select f;
This should result in something like:
SELECT [t1].[FooId],
[t1].[ParentFooId],
[t1].[Name]
FROM [dbo].[Foos] AS [t1]
WHERE [t1].[ParentFooId] IN (SELECT [t0].[ParentFooId]
FROM [dbo].[Foos] AS [t0]
WHERE[t0].[Name] = #p0)
May differ a bit (possibly an Exists()depending on your model)...I don't have a profiler window handy.
Try this:
var siblings = DataContext.Foos.Where(a => a.FooID == 3)
.Select(b => Foos.Where(b => Foos.ParentFooID == a.ParentFooID));
Assert.AreEqual(3, siblings.Count());
Hey all. I'm not sure how I could express the following query in C# using Linq to SQL. Here is a short snippet of the pure SQL:
select t1.WholesalerID, t1.RetailerID,
sum(t1.Col1) as 'Column 1',
sum(t2.Col1) as 'Other Column 1',
(sum(t1.Col1) - sum(t2.Col1)) as 'Column 1 Difference',
sum(t1.Col2) as 'Column 2',
sum(t2.Col2) as 'Other Column 2',
(sum(t1.Col2) - sum(t2.Col2)) as 'Column 2 Difference'
from Table1 t1
inner join Table2 t2 on t1.WholesalerID = t2.WholesalerID
group by t1.WholesalerID, t1.RetailerID
Now, I've done Linq to SQL joins and group by's before, but I'm sure how to go about doing these together. I run into the problem when I'm attempting to sum the values from the joined tables. Thanks.
I've reached this solution: (didn't tested it though)
var qry = from t in
(from t1 in Table1
join t2 in Table2 on t1.WholesalerID equals t2.WholesalerID
select new { t1, t2 })
group t by new { t.t1.WholesalerID, t.t1.RetailerID } into g
select new MyTypeWithDifferenceProp
{
WholesalerID = g.Key.WholesalerID,
RetailerID = g.Key.RetailerID,
Column1 = g.Sum(e => e.t1.Col1),
OtherColumn1 = g.Sum(e => e.t2.Col1),
Column2 = g.Sum(e => e.t1.Col2),
OtherColumn2 = g.Sum(e => e.t2.Col2),
};
This MyTypeWithDifferenceProp would have the Column1Difference and Column2Difference already defined.