How to convert Group By SQL to LINQ? - c#

I have the following SQL statement I'm trying to convert to Entity Framework.
SELECT S_NUMBER,A_NUMBER,FIRST_NAME,LAST_NAME
FROM EMPLOYEE WHERE S_NUMBER IN (
SELECT S_NUMBER
FROM EMPLOYEE
WHERE CO='ABC'
GROUP BY S_NUMBER
HAVING COUNT(*) > 1)
I've done some searching on using Group By in LINQ as well as sub-queries. I'm using LinqPad with a "C# Statement" and I came up with the following which based on some examples I found looks like it should work. However, I'm getting errors when trying to assign esn.S_NUMBER to sNumber in the anonymous object. The message says 'IGrouping' does not contain a definition for 'S_NUMBER'.
var result = from e in EMPLOYEE
where e.CO=="ABC"
group e by e.S_NUMBER into esn
select new
{
sNumber = esn.S_NUMBER
};
result.Dump();
I was under the impression that all the records would basically get put into a temp table called esn and I could be able to call the temptable.column name to assign it to my object that I will eventually return as a list.

You want to use Key instead of S_NUMBER. When grouping, the results get put into a IEnumerable<IGrouping>>. The grouping has a Key property which holds the key for that group, which in this case it's your S_NUMBER.
select new
{
sNumber = esn.Key
};
The following query should be a translation of the original SQL query. Instead of using a subquery, we're grouping and doing another from...in to "flatten" the sequence, and also checking that each grouping has a count > 1 like the original query.
var result = from e in EMPLOYEE
where e.CO=="ABC"
group e by e.S_NUMBER into esn
from e2 in esn
where esn.Count() > 1
select new
{
e.S_NUMBER,
e.A_NUMBER,
e.FIRST_NAME,
e.LAST_NAME
};

Since you're using the results of one query to filter another we can do a fairly direct transliteration of the query like so:
var result =
from e in EMPLOYEE
join f in (
from fe in EMPLOYEE
where fe.CO == 'ABC'
group null by S_NUMBER into grp
where grp.Count() > 1
select grp.Key
)
on e.S_NUMBER equals f
select new { e.S_NUMBER, e.A_NUMBER, e.FIRST_NAME, e.LAST_NAME };
Not only does this look a lot more like the original query but it should perform a bit faster (on MS SQL at least, can't speak for others) than the other form that might be simpler in LINQ but is much more complex when converted to SQL... four selects and a cross join, in my test version, vs two selects and an inner join for this one.
Of course if you prefer you can pull the inner query out as a separate IQueryable for clarity:
var filter =
from e in EMPLOYEE
where e.CO == 'ABC'
group null by S_NUMBER into grp
where grp.Count() > 1
select grp.Key;
var result =
from e in EMPLOYEE
join f in filter
on e.S_NUMBER equals f
select new { e.S_NUMBER, e.A_NUMBER, e.FIRST_NAME, e.LAST_NAME };

Related

LINQ - query on query results (some complex one)

Need some help in writing LINQ from the following SQL.
The main problem is double grouping.
I'm stacked in the second grouping
group by s.[e-year], s.[e-month]
Don't know how to implement.
Thanks a lot.
select s.[e-year], s.[e-month], count(s.projectid) 'projects entranced',
---------------------------------------------
(select count(subquery.CustomerTypeID) from
(select count(ap.ProjectID) as 'count', c.CustomerTypeID FROM Logging_ProjectsEntrances] pe
inner join users u on pe.userid = u.userid
inner join Companies c on u.CompanyId = c.CompanyID
inner join AssignedProjects up on pe.ProjectID = up.ProjectID
inner join Projects p on up.ProjectID = p.ProjectID
where ap.ProductID = 1 and year(pe.EntranceDate) = s.[e-year] and MONTH(pe.entrancedate) = s.[e-month] and c.CustomerTypeID = 2
group by ap.ProjectID, c.CustomerTypeID) subquery
group by subquery.CustomerTypeID
)
--------------------------------------------
from
(
select YEAR(pe.EntranceDate) as 'e-year', MONTH(pe.EntranceDate) as 'e-month', up.ProjectID as 'projectid'
FROM Logging_ProjectsEntrances pe
inner join AssignedProjects ap on pe.ProjectID = ap.ProjectID
inner join Projects p on ap.ProjectID = p.ProjectID
where ap.ProductID = 1
group by year(pe.EntranceDate), month(pe.EntranceDate), ap.ProjectID
) as s
group by s.[e-year], s.[e-month]
order by s.[e-year] desc , s.[e-month] desc
For translating SQL to LINQ query comprehension:
Translate FROM subselects as separately declared variables.
Translate each clause in LINQ clause order, translating monadic operators (DISTINCT, TOP, etc) into functions applied to the whole LINQ query.
Use table aliases as range variables. Use column aliases as anonymous type field names.
Use anonymous types (new { ... }) for multiple columns.
Left Join is simulated by using a into join_variable and doing another from from the join variable followed by .DefaultIfEmpty().
Replace COALESCE with the conditional operator and a null test.
Translate IN to .Contains() and NOT IN to !...Contains()
SELECT * must be replaced with select range_variable or for joins, an anonymous object containing all the range variables.
SELECT fields must be replaced with select new { ... } creating an anonymous object with all the desired fields or expressions.
Proper FULL OUTER JOIN must be handled with an extension method.
Note: Your SQL query is using a SQL trick (SELECT x ... GROUP BY x) to perform the equivalent of a DISTINCT, which should be used as it expresses the intent more clearly.
So, for your SQL query:
var subq = (from pe in projectsEntrances
join ap in assignedProjects on pe.ProjectID equals ap.ProjectID
join p in projects on ap.ProjectID equals p.ProjectID
where ap.ProductID == 1
select new { e_year = pe.EntranceDate.Year, e_month = pe.EntranceDate.Month, ap.ProjectID }).Distinct();
var ans = from s in subq
group s by new { s.e_year, s.e_month } into sg
orderby sg.Key.e_year descending, sg.Key.e_month descending
select new { sg.Key.e_year, sg.Key.e_month, ProjectsEntranced = sg.Count() };

SQL inner select to LINQ

This thing is driving me crazy.
I think its not that complicated but I don't get it.
I have this working SQL statement and I need the Linq statement for it.
select
a.id, a.date,
(select top 1 b.price from b where a.id = b.id and a.date >= b.date) as price
from a;
For explanation:
I have a table a with articles and a table b with a price history.
Now I need a datagrid where I can enter new entries for table a (so a view is not working) and after saving its showing me associated price
I hope I could express myself understandably
For translating SQL to LINQ query comprehension:
Translate FROM subselects as separately declared variables.
Translate each clause in LINQ clause order, leaving monadic operators (DISTINCT, TOP, etc) as functions applied to the whole LINQ query.
Use table aliases as range variables. Use column aliases as anonymous type field names.
Use anonymous types (new { }) for multiple columns
Left Join is simulated by using a into join_variable and doing another from from the join variable followed by .DefaultIfEmpty().
Replace COALESCE with the conditional operator and a null test.
SELECT * must be replaced with select range_variable or for joins, an anonymous object containing all the range variables.
SELECT fields must be replaced with select new { ... } creating an anonymous object with all the desired fields or expressions.
Proper FULL OUTER JOIN must be handled with an extension method.
For your query:
var ans = from ra in a
select new {
ra.id,
ra.date,
price = (from rb in b
where ra.id == rb.id && ra.date >= rb.date
select rb.price).First()
};
I'm not sure which syntax you're aiming for, but one of these should do the trick. I haven't tested it though.
from xa in a
select new
{
id,
date,
price = (
from xb in b
where xa.id == xb.id && xa.date >= xb.date
select xb.price
).First() // or .FirstOrDefault() if you want to allow null prices
};
or
a.Select(xa => new
{
id,
date,
price = b.First(xb => xa.id == xb.id && xa.date >= xb.date) // or .FirstOrDefault() if you want to allow null prices
});

Nested Select MySQL statements to LINQ

I'm trying to convert the following MySQL statement in to LINQ query format
SELECT * FROM table1 WHERE table1.id IN (SELECT c_id FROM table2 WHERE a_id IN (1, 49) GROUP BY c_id HAVING COUNT(*) = 2) ORDER BY name
Got as far as this, but I'm drawing a blank on how to handle the IN and 2nd SELECT statement
myItems = from c in table1
let id = c.id
where ????
orderby c.name
select c;
Would appreciate some guidance with this please
Try this:
var ids=new[]{1,49};
var innerquery=table2.Where(e=>ids.Contains(e.a_id))
.GroupBy(e=>e.c_id)
.Where(g=>g.Count()==2)
.Select(g=>g.Key);
var myItems = from c in table1
where innerquery.Contains(c.id)
orderby c.name
select c;
First define your inner query,after the group by you will get a collection of IGrouping<TKey, TElement>> that represent a collection of objects that have a common key, filter the groups choosing only those where count==2, and select the keys of those groups. The second part is really easy to understand. I split the process in two queries to do it more readable, but you can merge both query in one.

LINQ query in C# using group and join

I am trying to write the following SQL query in LINQ C# but am not able to get hold of the columns after the group clause.
SQL query
/*get the number of questions by subject*/
select b.SubjectID, b.SubjectName, count(*) AS count
from QuestionsTable a, SubjectTable b
where a.SubjectID is not null AND a.SubjectID = b.SubjectID
GROUP BY a.SubjectID
LINQ Query
var result =(from a in db1.QuestionsTables
join b in db1.SubjectTables
on a.SubjectID equals b.SubjectID
where a.SubjectID != null
group a by a.SubjectID into g
select new { a.QuestionID, a.SubjectID, b.SubjectName
}).ToList();
Your SQL wouldn't be executed correctly cause you query groups by a.Subject.ID, but selects b.SubjectID, and b.SubjectName. Usually you should also include single selected fields to GROUP BY list.
(As far as I know, some SQL servers can process functional dependent fields, so they could process your query. But in general it's wrong).
So your working query should be:
SELECT b.SubjectID, b.SubjectName, COUNT(*) AS Count
FROM QuestionsTable a, SubjectTable b
WHERE a.SubjectID is not null AND a.SubjectID = b.SubjectID
GROUP BY b.SubjectID, b.SubjectName
And your LINQ should be
from a in db.QuestionsTable
join b in db.SubjectTable
on a.SubjectId equals b.SubjectId
where a.SubjectId != null
group b by new { b.SubjectId, b.SubjectName } into g
select new { g.Key.SubjectId, g.Key.SubjectName, g.Count() }
Although it could be made the way you asked, the same result can be obtained in much simpler natural way, not involving grouping at all, like this
// get the number of questions by subject
var result =
(from s in db1.SubjectTables
select new
{
s.SubjectID,
s.SubjectName,
Count = db1.QuestionsTables.Count(q => q.SubjectID == s.SubjectID)
}).ToList();
Update: Regardless of downvotes, I strongly believe this is the right way to solve this particular problem - simple and natural. Why group something that is already grouped (by primary key).

LINQ Group by and having where clause

Below is the SQL Query I am trying to translate
SELECT dbo.Contracts.Supplier
FROM dbo.Contracts INNER JOIN dbo.Products ON dbo.Contracts.Product = dbo.Products.Product
where dbo.Products.ProductGroup='Crude'
GROUP BY dbo.Contracts.Supplier
Am I doing something wrong because I do not get same results with the following LINQ
var result = from c in context.Contracts
join p in context.Products on c.Product equals p.Product1
where p.Product1.Equals("Crude")
group c by c.Supplier into g
select new { supplier = g.Key };
It is generating a weird statement
SELECT
1 AS [C1],
[Distinct1].[Supplier] AS [Supplier]
FROM ( SELECT DISTINCT
[Extent1].[Supplier] AS [Supplier]
FROM [dbo].[Contracts] AS [Extent1]
WHERE N'Crude' = [Extent1].[Product]
) AS [Distinct1]
Using distinct would work but to get same results, LINQ should be generating a statement like so (it's like it is ignoring the join):
SELECT distinct dbo.Contracts.Supplier
FROM dbo.Contracts INNER JOIN dbo.Products ON dbo.Contracts.Product = dbo.Products.Product
where dbo.Products.ProductGroup='Crude'
I'm assuming that you are using 'EntityFramework' or 'Linq To SQL'. If so, you should be able to use navigation properties to navigate to product and filter invalit results out. This way your query might look something like this:
var result = (from c in context.Contracts
where c.Products.Any(p => p.ProductGroup == "Crude")
select c.Supplier).Distinct();
It will automatically convert into correct query (in this case possibly without join even, just using Exists sql keyword) and return distinct suppliers. This is if I understand your objective correctly - you want to obtain all suppliers assigned to contracts that contain product from 'Crude' product group.
Basically you should try to avoid using joins from linq to sql or linq to entities as much as possible when you can use navigation properties. System will probably be better at converting them into specific sql.

Categories