LINQ Inner join with Left outer join to get non matching values - c#

I am trying to convert below SQL query to LINQ,
SELECT
TD.*, RD.*
FROM
dbo.TransactionDetail TD
INNER JOIN
dbo.Measure M ON M.InternalID = TD.MetricCode
LEFT OUTER JOIN
(SELECT
tmp.ID, tmp.ReportingDate, 1 AS Match
FROM
tmp) AS RD ON RD.ID = M.Frequency
AND RD.ReportingDate = TD.ReportingDate
WHERE
RD.Match IS NULL
AND TD.BatchID = 'e07f9855-b286-4406-9189-5cfb2a7914c8'
My Linq query looks like below,
Update : Added td and rd definition
var rd = (from tt in result
select new { ID = tt.Id, tt.ReportingDate });
// inner join
var td = TransactionDetail.Join(
MesureTb,
t => t.MetricCode,
m => m.InternalId,
(t, m) => new
{
t.Id,
t.RowAction,
t.BatchId,
t.TrustCode,
t.MetricCode,
t.ReportingDate,
t.Value,
t.UpperBenchmark,
t.LowerBenchmark,
m.InternalId,
Frequency = m.Frequency
});
var result2 = from a in td//inner join already done in previous step
join b in rd
on new { ReportingDate = (DateTime)a.ReportingDate, ID = a.Frequency } equals new { b.ReportingDate, b.ID } into j
from b in j.DefaultIfEmpty()
where b == null && a.BatchId == batchId
select a.Id;
Could someone help me how I can write this in a simple and efficient way? It is basically selecting the Non matching rows where Match = null
Update :
The TD in Linq contains list of reporting dates ranging from 2009 to 2021
The Right table RD is a lookup table which has date values between 2010 to 2020
Expected out put : The query should return the values from TD table which has Reporting date < 2010 and > 2020
Example below :
Expected Output : As in above screen shot, I need values which have Match as NULL.
I hope this explains what I am trying to achieve.
Any help is appreciated.
Thanks in advance.

Related

Having Trouble Converting SQL to LINQ when using a join on a select statement

Folks,
Trying to convert the following SQL-Server Command to LINQ. I have verified the SQL runs correctly via SSMS.
select top 100 tts.* from tblLCState tts
INNER JOIN
(SELECT fldLCID, MAX(fldStateDate) AS Statedate
FROM tblLCState
GROUP BY fldLCID) grptts
ON tts.fldLCID = grptts.fldLCID
AND tts.fldStateDate = grptts.Statedate
where fldLCStateCode = 1
order by fldStateDate desc
I am confused how to join the table tblLCState to the select statement. My attempt at the LNIQfollows:
from tRow in tblLCState
join irow2 in (from iRow in tblLCState
group iRow by iRow.fldLCID into g
select new {fldLCID = g.Key, MaxStateDate = (from t2 in g select t2.fldStateDate).Max()} )
on ((tRow.fldStateDate = irow2.MaxStateDate) and (tRow.fldLCID = irow2.g.fldLCID))
The error is on the and operator in the on clause saying that a ) was expected. I have not attempted the where/order/top 100 at this point. Just have spent much time looking for the join with no luck on this form or any other. I have seen many posts to join on another table but unfortunately I don't have this luxury.
Any help would be appreciated.
Thanks
Tom D.
LINQ
var result = (from tRow in tblLCState
join irow2 in (from iRow in tblLCState
group iRow by iRow.fldLCID into g
select new { fldLCID = g.Key, MaxStateDate = g.Max(k => k.fldStateDate) })
on new { StateDate = tRow.fldStateDate, tRow.fldLCID } equals new { StateDate = irow2.MaxStateDate, irow2.fldLCID }
select tRow);

Generate a report from Northwind DB using Linq

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.

LINQ to EF, Left Join and group by clause

I have this SQL:
select o.prod_id, SUM(o.[count]) as [count]
into #otgr
from otgr o
where o.[date]<= #date
group by o.prod_id
select f.prod_id, SUM(f.[count]) as [count]
into #factory
from factory f
where f.[date]<= #date
group by f.prod_id
select p.name, p.id, f.[count] - ISNULL(o.[count],0) as av_count
from products p
join #factory f on f.prod_id = p.id
left join #otgr o on o.prod_id = p.id
where f.[count] - ISNULL(o.[count],0) > 0
How can I translate this into Linq? I'm stuck with this code:
from otgrr in db.otgr
where otgrr.date <= date
group otgrr by otgrr.prod_id into otgrs
from fac in db.factory
where fac.date <= date
group fac by fac.prod_id into facs
from prod in db.products
join fac2 in facs on prod.id equals fac2.Key
join otg2 in otgrs.DefaultIfEmpty(new {id = 0, av_count = 0 }) on prod.id equals otg2.Key
where (fac2.SUM(a=>a.av_count) - otg2.SUM(a=>a.av_count)) > 0
select new products { id = prod.id, name = prod.name, av_count = (fac2.SUM(a=>a.av_count) - otg2.SUM(a=>a.av_count))
Thank to everyone, and sorry for my bad english
You can also check LINQPad.
Of course, you can split this into multiple LINQ queries (after all, the execution is deferred, so it will be executed all as one single query, without using temporary tables. It should be faster in 99% of the cases).
But in your case it can be written more simply, by using navigation properties you probably have already set up:
var result= from p in products
select new {Name=p.Name,
Id = p.Id,
Count = p.Factories.Where(f=> f.date <= date).Sum(f=>f.Count)
- p.otgrs.Where(o=> o.date <= date).Sum(o=>o.Count)
};

LINQ to SQL join with LINQ to DataSet

I have a SQL database that I'm using LINQ to connect to (LINQ To SQL) and a local set of XML data (in a dataset). I need to perform an outer left join on the SQL table "tblDoc" and dataset table "document" on the keys "tblDoc:sourcePath, document:xmlLink". Both keys are unfortunately strings. The code I have below doesn't return any results and I've tried a few variations but my LINQ skills are limited. Does anyone have any suggestions or alternate methods to try?
DataColumn xmlLinkColumn = new DataColumn(
"xmlLink",System.Type.GetType("System.String"));
xmlDataSet.Tables["document"].Columns.Add(xmlLinkColumn);
foreach (DataRow xmlRow in xmlDataSet.Tables["document"].Rows)
{
xmlRow["xmlLink"] = (string)xmlRow["exportPath"] +
(string) xmlRow["exportFileName"];
}
var query =
from t in lawDataContext.tblDocs.ToList()
join x in xmlDataSet.Tables["Document"].AsEnumerable()
on t.SourceFile equals (x.Field<string>("xmlLink"))
select new
{
lawID = t.ID,
xmlID = x == null ? 0 : x.Field<int>("id")
};
foreach (var d in query.ToArray())
{
Debug.WriteLine(d.lawID.ToString() + ", " + d.xmlID.ToString());
}
The join clause produces standard inner join behavior. To get an outer join, you need to use the DefaultIfEmpty() extension method:
var query = from t in lawDataContext.tblDocs.ToList()
join x in xmlDataSet.Tables["Document"].AsEnumerable()
on t.SourceFile equals (x.Field<string>("xmlLink"))
into outer
from o in outer.DefaultIfEmpty()
select new
{
lawID = t.ID,
xmlID = o == null ? 0 : o.Field<int>("id")
};

Left Join If A Certain Field Is A Certain Value?

I'm having a difficult time building a left join query that does something like this:
var Results =
from a in Db.Table1
join b in Db.Table2 on a.Id equals b.Id into c //IF b.Value2 == 1
from d in c.DefaultIfEmpty()
select new {Table1 = a};
The problem I'm having is that I simply can't add "where b.Value2==1" before the "select" because this will exclude all joined rows with b.Value2 != 1.
What I want to have happen is for the columns to join only if b.Value2==1, and if not, it will keep the row (instead of excluding them as the where described above would), but just not join the two columns.
I hope this makes sense and there is a way to do this, thanks!
Edit:
Table1
ValueA=1,ValueB=1
ValueA=2,ValueB=2
Table2
ValueB=1,ValueC=1
ValueB=2,ValueC=2
Desired Result (joining on Table1.ValueB==Table2.ValueB Where Table2.ValueC == 2, still include this row, but just don't join it to Table2):
ValueA=1, ValueB=1, ValueC=null //ValueC=null because where Table2.ValueB==1,ValueC != 2
ValueA=2, ValueB=2, ValueC=2
This should work, basically create a dummy var and join on it. The problem is this doesn't make for a friendly push down operation to the DB.
var Results =
from a in Db.Table1
join b in Db.Table2 on
new { ID= a.Id, Bit = true }
equals
new { ID = b.Id, Bit = b.Value ==1 }
into c
from d in c.DefaultIfEmpty()
select new {
Table1 = a,
Table2= d //will be null if b.Value is not 1
};
add the where, then add another query that's just for b.Value2 != 1, merge both queries?
Create two linq queries, which will be composed into one sql query:
The first query will do a regular outer join
var partResult= from a in Db.Table1
join b in Db.Table2 on a.Id equals b.Id into c
from d in c.DefaultIfEmpty()
select new { Table1 = a, Table2=d};
The second query will select all entries from the first query where Table2 is null or Table2.Id==1
var result = from obj in partResult
where (obj.Table2 == null || obj.Table2.Id == 2)
select obj.Table1;
This will be composed into a single select statement when the queries execute:
SELECT [t0].[Id]
FROM [dbo].[Table1] AS [t0]
LEFT OUTER JOIN (
SELECT 1 AS [test], [t1].[Id]
FROM [dbo].[Table2] AS [t1]
) AS [t2] ON [t0].[Id] = [t2].[Id]
WHERE ([t2].[test] IS NULL) OR ([t2].[Id] = 2)
if Table 1 has Ids 1, 2, 3, 4, 5, 6 and Table2 has Ids 1, 2, 3, 4 the result will be 1,5,6

Categories

Resources