LINQ to Entiites, left outer join of a group by - c#

I can build a SQL query easily that does the following.
For each row of 'Table1' I want a count of the number of related 'Table2' records that have a 'Status' not equal to 5. There might be no matches so I use a 'left outer join' and then within that use a 'group by' in order to find the total number of matches.
The following query works as SQL. It outputs either a null for the count because there are no matches at all or an actual integer count if there is at least one match.
select
Table1.Id,
Table2Outer.Count
from
Table1
left outer join
(
select
Table2.Id,
COUNT(*) as Count
from
Table2
where
Table2.Status != 5
group by
Table2.Id
) as Table2Outer on Table2Outer.Id = Table.Id
Unfortunately I cannot work out how to convert this to LINQ to Entities. The following does not even compile and I am stuck!
var x = (from t1 in ctx.Table1
join t2 in ctx.Table2 on { t1.Id, t2.Status } equals new { t2.Id, Status != 5 } into t2Outer
from t2OuterB in t2Outer.DefaultIfEmpty()
group t2Outer by ?);
Any ideas?

Use a subquery to count child records in the projection.
var x = from t1 in ctx.Table1
select new
{
t1.Id,
Count = (from t2 in ctx.Table2
where t2.Status != 5
where t2.Id == t1.Id
select t2).Count()
};

Related

How to Select all records from table 1 and exclude matching records from table 2 using LINQ

I want to select all records from table 1 and if there any match between table 1 and table 2 then I have to exclude that records.
Note: The requirement is I have to use left outer join for that..
For example,
table 1 table 2
1 1
2 2
3
Output should be 3
I have written query in SQL but I want this in SQL LINQ like from a in dbContext.Table1....
select t1.*
from table1 t1
left outer join table2 t2 on t1.ID = t2.ID and t1.Code = t2.Code
where s.ID is null
How to solve this?
var result = (from primer in one
join primer2 in two on primer.id equals primer2.id into gj
where gj.Count()==0
select new
{
primer.id
}).ToList();
Where ONE - it is first table, TWO - it is second table.
Instead of
...select new
{
primer.id
}...
You can use
...
select primer
...

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() };

Select In Select Statement

What is an equivalent LINQ query to the following SQL query:
Select Id, Name
From Table1 tbl1
Where Id in ( Select Id From Table2)
You basically described an inner join with selection of rows from one table:
var result = from t1 in Table1
join t2 in Table2
on t1.Id equals t2.Id
select new{t1.Id, t1.Name};
Next SQL statement will be generated using EF and MS SQL:
SELECT t1.Id, t1.Name
FROM Table1 AS t1
INNER JOIN Table2 AS t2 ON t1.Id = t2.Id
Note, that if you are selecting non-unique items from Table2, you can potentially have duplicates in the result. Use next query to avoid this problem, cons: it loads all ids from Table2 into memory, pros: more time efficient. Checkout Felipes answer's, which is also quite good, but has it's own cons discussed in the comment section.
var table2Ids = new HashSet<int>(context.Table2.Select(t2 => t2.Id));
var result = context.Table1
.Where(t => table2Ids.Contains(t.Id))
.Select(t => new{t.Id, t.Name});
try using the Contains method:
var query = from c in db.Table1
where db.Table2.Select(x => x.Id).Contains(c.Id)
select new { c.Id, c.Name };
var result = query.ToList();

Left outer join using LINQ - yields different outputs

I am trying to write left outer join using LINQ. The SQL looks like,
SELECT *
FROM Table1 td1
LEFT OUTER JOIN Table2
ON td1.ColumnName = td2.ColumnName
WHERE td2.ColumnName IS NULL
ORDER BY SomeColumns
If I run this query in SQL Query analyzer, it returns say 100 records. And my converted LINQ code returns 105 records.
I have written LINQ in 2 ways as,
Method 1:
var data= (from td1in Table1
join td2 in Table2.Where(a => a.ColumnName == (int?)null)
on td1.ColumnName equals td2.ColumnName into outer
from x in outer.DefaultIfEmpty()
orderby SomeColumns
select td1);
Method 2: This gives an exception as, failed to enumerate results
var data = from td1 in Table1
join td2 in Table2
on td1.ColumnName equals td2.ColumnName into outer
from item in outer.DefaultIfEmpty()
where item.ColumnName.Value == (int?)null
orderby somecolumns
select td1 ;
The column used in where clause is nullable int type.
The result returned in SQL analyzer seems to be correct one.
Please help me in getting the identical results.
Thanks
Try this query:
var data = from td1 in Table1
join td2 in Table2
on td1.ColumnName equals td2.ColumnName into outer
from item in outer.DefaultIfEmpty()
where item == null
orderby somecolumns
select td1 ;
In your original query that line item.ColumnName.Value == (int?)null was wrong, because you tried to retrieve value for all ColumnName even if item was null. I corrected it and now it should work fine.

How to rewrite this linq query correctly?

int id =2 ;
(from t1 in Table1
join t2 in Table2
on new { t1.id, id} equals new { t2.id, t2.otherid }
select t1).ToList();
Currently the above query gives me a compilation error saying
The type on one of the join expressions is incorrect.
As you can see in the above query I want to join on a single integer value as you would in sql. I want to these so that query is faster and I don't want to do a where at the end because that would mean it will get all the rows and then filter with the where clause. As I have lot of rows in both the tables it would be good if I can filter rows on join clause itself. Thanks for your help !
You need to use the same anonymous type (with the same property names) in both halves of the join:
on new { t1.id, otherId = 2 } equals new { t2.id, t2.otherId }
Your text implies that you actually want to join on a single value; if so, you don't need an anonymous type at all:
on t1.id equals t2.otherid
When you join using an anonymous classes, the members of those class' names must match. The problem is easily solved by adding names to your anonymous class' members:
int id = 2;
(from t1 in Table1
join t2 in Table2
on new { Id = t1.id, OtherId = id }
equals new { Id = t2.id, OtherId = t2.otherid }
select t1).ToList();
Although, the more I look at it the more I realize that the join doesn't need to be that complex. It looks like you're adding the static id in the join. You should be able to get away with it in the where clause which would reduce the join to a single value:
int id = 2;
(from t1 in Table1
from t2 in Table2
on t1.id equals t2.id
where t2.otherid = id
select t1).ToList();
I see two possible solutions:
This variant prefilters Table2, then performs the join.
int id =2;
(from t1 in Table1
join t2 in (from a in Table2 where a.otherid == id select a)
on t1.id equals t2.id
select t1).ToList();
Here is a debugged variant of your original code. Because the compiler uses the property names when creating anonymous objects, you have to be explicit about the names.
int id =2;
(from t1 in Table1
join t2 in Table2
on new { Id1 = t1.id, Id2 = id } equals new { Id1 = t2.id, Id2 = t2.otherid }
select t1).ToList();
HTH, Mark

Categories