Dynamic Left join 2 datatable - c#

I have 2 C# datatable:
Datatable A:
id
Name
Age
Height
01
Pauls
22
170
02
Sam
20
175
03
Mike
20
175
04
Jame
23
180
Datatable B:
id
Height
Age
01
175
23
02
190
21
The question here is how could I get this output by join 2 table A & B by id and get output datatable using Linq in C# OutputTable:
id
Name
Age
Height
01
Pauls
23(value in table B)
175 (value in table B)
02
Sam
21(value in table B)
190 (value in table B)
03
Mike
20
175
04
Jame
23
180
My code here:
var results = from ta in tableA.AsEnumerable()
join tb in tableB.AsEnumerable on ta["id"] equals tb["id"]
select new
{
ta["id"],
ta["Name"],
tb["Age"],
tb["Height"]
};
My output so far (not what I expected):
id
Name
Age
Height
01
Pauls
23(value in table B)
175 (value in table B)
02
Sam
21(value in table B)
190 (value in table B)
P/s: above table is just an example data, true data is bigger

INNER JOIN works as filter if record not found on the right side. You have to use LEFT JOIN here, which is implemented in LINQ as GroupJoin with SelectMany:
var results = from ta in tableA
join tb in tableB on ta.id equals tb.id into g
from tb in g.DefaultIfEmpty()
select new
{
ta.id,
ta.Name,
Age = tb != null ? tb.Age : ta.Age,
Height = tb != null ? tb.Height : ta.Height,
};

Could you try something like this:
var results = from ta in tableA
join tb in tableB on ta.id equals tb.id into gj
from subTableA in gj.DefaultIfEmpty()
select new
{
id = ta.id,
Name = ta.Name,
Age = (subTableA == null ? ta.Age : tb.Age + "(value in table B)"),
Height = (subTableA == null ? ta.Height : tb.Height + "(value in table B)")
};
Here is some page about left join using Entity Framework - https://learn.microsoft.com/en-us/dotnet/csharp/linq/perform-left-outer-joins
Idea is to introduce row 'from subTableA in gj.DefaultIfEmpty()' in order to get all rows from the join, even those which don't have matching value in second table.

If I understand the question correctly, you want to get back a DataTable from your LINQ query. However, LINQ returns an IEnumerable<T> or an IQueryable<T> if you're querying a database.
If you want a DataTable, you'll need some additional code. I suggest you refer to the top answer on this StackOverflow question, which provides a solution for converting an IEnumerable to a DataTable.

Related

How to get closest smallest number using linq C#

I have a sql table like this,
SomeDouble SomeInt
1.00 121
1.50 124
2.00 200
2.50 321
and so on... up to 10,000 for SomeDouble
Now I can have a decimal number anywhere between 0.0 to 10,000.00 and I need to find the correct row for it. For example if number is 1.12 then I want it to return 121.
1.49 should return 121, 1.50 should return 124, 1.51 should return 124.
Trimmed version of what I am trying is,
var myValue = 1.12
var SomeInt = (from mainTable in table1
join table3 in table2 on mainTable.someId equals table3.someId
where table3.Column1 == Column1 && mainTable.SomeDouble >= myValue
select mainTable.SomeInt).FirstOrDefault();
but my output is 124. How can I change above to get me the closest smallest number then myValue ?
Because the SomeDouble values are integers and half-integers, you can round up myValue to the next multiple of 0.5:
var myValueToLookUp = Math.Ceiling(myValue * 2) / 2;
and then look up the value of SomeInt directly with mainTable.SomeDouble == myValueToLookUp to avoid any confusion or inefficiency with <= or >=.
In SQL, you can express the closest as:
select t.*
from t
order by abs(SomeDouble - 1.12)
fetch first 1 row only;
A more efficient method would narrow it down to two rows first:
select t.*
from ((select t.*
from t
where t <= 1.12
order by SomeDouble desc
fetch first 1 row only
) union all
((select t.*
from t
where t > 1.12
order by SomeDouble asc
fetch first 1 row only
)
) t
order by (SomeDouble - 1.12)
fetch first 1 row only;
Using Linq queries:
var above = (from mainTable in table1
join table3 in table2 on mainTable.someId equals table3.someId
where table3.Column1 == Column1 && mainTable.SomeDouble >= myValue
orderby mainTable.SomeDouble
select new {SomeInt = mainTable.SomeInt, SomeDouble = mainTable.SomeDouble}).FirstOrDefault();
var below = (from mainTable in table1
join table3 in table2 on mainTable.someId equals table3.someId
where table3.Column1 == Column1 && mainTable.SomeDouble < myValue
orderby mainTable.SomeDouble descending
select new {SomeInt = mainTable.SomeInt, SomeDouble = mainTable.SomeDouble}).FirstOrDefault();
int SomeInt;
if (above == null)
SomeInt = below.SomeInt;
else if (below == null)
SomeInt = above.SomeInt;
else if (Math.Abs(below.SomeDouble - myValue) <= Math.Abs(above.SomeDouble - myValue))
SomeInt = below.SomeInt;
else
SomeInt = above.SomeInt;
Here's the linq extension method to order the records by absolute difference of SomeDouble, then by SomeInt to get the smallest first for 2 or more matches, and then we get the first one. It looks like both columns exist on main table, so I'm guessing we can limit that first then join whatever you want to it.
mainTable.OrderBy(x => Math.Abs(x.SomeDouble - myValue)).ThenBy(x => x.SomeInt).First()
If you can do it in SQL, then
SELECT COALESCE(MAX(SomeInt), 0)
FROM DoubleToInt
WHERE SomeDouble <= 1.12

Merge 2 rows into one row

I have this query :
Select ID, Groucode
from mytable
which returns this result:
ID GroupCode
---------------
60 00
60 01
70 00
80 00
80 01
90 00
What I want is to shows only the rows with '00' and '01', like this:
ID GroupCode
---------------
60 00
60 01
80 00
80 01
We can use aggregation here:
WITH cte AS (
SELECT ID
FROM mytable
WHERE GroupCode IN ('00', '01')
GROUP BY ID
HAVING COUNT(DISTINCT GroupCode) = 2
)
SELECT *
FROM mytable
WHERE ID IN (SELECT ID FROM cte);
Demo
Note that if the GroupCode column is really numeric, then displaying your data as 00 and 01 is not reflective of the underlying data, because SQL Server will really just treat those values as 0 and 1. In that case, you may modify my above query slightly.
I am a fresher to SQL Server , Please find my answer for above mentioned concept and you can make me correct if i am wrong .
Select mytable.* from mytable
inner join
(select id from mytable group by id having count(id)>1) as table2 on mytable.id = table2.id

LINQ inner join condition

Suppose I have the following tables:
**Members**
Code Name
001 Sue
002 Peter
003 John
**Sales Info**
MemCode Date Type (A/B) Values
001 17/12/2013 A 100
001 17/11/2013 B 100
002 16/12/2013 A 100
I want to have the following result table
**Member Sales in 2013**
MemCode Jan(A) Jan(B) Feb(A) ... Nov(B) Dec(A) Dec(B)
001 0 0 0 100 100 0
002 0 0 0 0 100 0
I tried to exact some data (The Nov(A) and Nov(B)) first using the query,
var query = from tb in Members
join tb2 in SalesInfo on tb.MemCode equals tb2.MemCode
join tb3 in SalesInfo on tb.MemCode equals tb3.MemCode
where tb2.Type.Equals("A") &&
tb2.Date.Month.Equals(11)
tb3.Type.Equals("B") &&
tb3.Date.Month.Equals(11)
Select ...
However it returns no data as no A record found in November so the whole row is filtered. Is there any suggestion to solve the problem?
The problem is you are asking for (Type==A && Type==B) and it is impossible. You can select A first, and get B value of same date in a subquery.
Indeed, if you want type A and type B, you should select on type == A OR type == B. No row will ever satisfy both conditions :)
Something like this is simpler and probably more effective
var query = from tb in Members
join tb2 in SalesInfo on tb.MemCode equals tb2.MemCode
where (tb2.Type.Equals("A") ||
tb2.Type.Equals("B")) &&
tb2.Date.Month.Equals(11)
Select ...

C# LINQ Join Error

SELECT PART_TYPE.PART_TYPE_ID,
PART_TYPE.PART_TYPE_NAME,
PART_AVAILABILITY.DATE_REF,
PART_TYPE.VEHICLE_ID,
PART_AVAILABILITY.AVAIL_COUNT
FROM PART_AVAILABILITY
RIGHT JOIN PART_TYPE
ON PART_AVAILABILITY.PART_TYPE_ID = PART_TYPE.PART_TYPE_ID
AND PART_AVAILABILITY.VEHICLE_ID = PART_TYPE.VEHICLE_ID
where PART_TYPE.VEHICLE_ID = 366
PART_TYPE_ID and VEHICLE_ID are Primary Key in PART_TYPE table.
VEHICLE_ID is a Foreign Key from VEHICLE table.
DATE_REF, PART_TYPE_ID and VEHICLE_ID are Primary Key in PART_AVAILABILITY table.
VEHICLE_ID and PART_TYPE_ID are Foreign Key from PART_TYPE table.
Above query gave below output.
PART_TYPE_ID PART_TYPE_NAME DATE_REF VEHICLE_ID AVAIL_COUNT
5 A1 2013-06-20 00:00:00.000 366 1
6 B1 2013-06-20 00:00:00.000 366 2
7 C1 2013-06-20 00:00:00.000 366 1
8 D1 NULL 366 NULL
9 E1 NULL 366 NULL
16 F1 2013-06-20 00:00:00.000 366 1
This my linq query for above sql query.
var vehiclePartType = from pa in context.PART_AVAILABILITY
join pt in context.PART_TYPE
on pa.PART_TYPE_ID equals pt.PART_TYPE_ID into joined
from j in joined.DefaultIfEmpty()
where j.VEHICLE_ID == 366
select new
{
PART_TYPE = j,
PART_AVAILABILITY = pa
};
But linq query gave below output.
PART_TYPE_ID PART_TYPE_NAME DATE_REF VEHICLE_ID AVAIL_COUNT
5 A1 2013-06-20 00:00:00.000 366 1
6 B1 2013-06-20 00:00:00.000 366 2
7 C1 2013-06-20 00:00:00.000 366 1
16 F1 2013-06-20 00:00:00.000 366 1
NULL records are missing.
How can i solve this ?
Right Join in LINQ is done by reversing join statements so correct one would be this:
var vehiclePartType = from pt in context.PART_TYPE
join pa in context.PART_AVAILABILITY on pt.PART_TYPE_ID equals pa.PART_TYPE_ID into joined
from j in joined.DefaultIfEmpty()
where pt.VEHICLE_ID == 366
select new
{
PART_TYPE = pt,
PART_AVAILABILITY = j
};
I think You have to swap your tables. Actually what you are doing is left join . To convert into right just swap the tables Like::
{
var vehiclePartType = from
pt in context.PART_TYPE join pa in context.PART_AVAILABILITY
on pt.PART_TYPE_ID equals pa.PART_TYPE_ID into joined
from j in joined.DefaultIfEmpty()
where j.VEHICLE_ID == 366
select new
{
PART_TYPE = j,
PART_AVAILABILITY = pa
};
}
I am not too much familier with LINQ but in your SQL query you have
AND PART_AVAILABILITY.VEHICLE_ID = PART_TYPE.VEHICLE_ID
This is missing in your LINQ query.

Query to return pairs of related rows, but only when both rows exist

I have the following tables:
*sistema_documentos*
[id], [caminho], [idDocType](FK -> sistema_DocType.id)
*sistema_Indexacao*
[id] ,[idDocumento](FK -> sistema_documentos.id) ,[idIndice](FK ->
sistema_Indexes) ,[valor]
*sistema_DocType*
[id], [tipoNome](FK -> sistema_DocType.id)
*sistema_DocType_Index*
[id],[idName],[mask],[idTipo](FK -> sistema_DocType.id),[tamanho]
From this query:
select distinct a.id, b.idIndice, b.valor from tgpwebged.dbo.sistema_Documentos as a
join tgpwebged.dbo.sistema_Indexacao as b on a.id = b.idDocumento
join tgpwebged.dbo.sistema_DocType as c on a.idDocType = c.id
join tgpwebged.dbo.sistema_DocType_Index as d on c.id = d.docTypeId
where d.docTypeId = 40
and (b.idIndice = 11 AND b.valor = '11111111' OR b.idIndice = 12 AND b.valor = '22222' )
I get the following result
id idIndice valor
13 11 11111111
13 12 22222
14 11 11111111
14 12 22222
16 12 22222
As you can see, I want all ids with idIndice 11 with value 11111111 and 12 with value 22222
Id 16 has id 12 with value 22222 authough it does not have id 11 with value 11111111 so I donĀ“t want it to be shown.
How can I update my query to obtain the result I want. Hope my question is clear. If it is not just ask and I edit my post. Thanks
I would suggest something like this:
WITH TempTable AS
(
select distinct a.id, b.idIndice, b.valor
from tgpwebged.dbo.sistema_Documentos as a
join tgpwebged.dbo.sistema_Indexacao as b on a.id = b.idDocumento
join tgpwebged.dbo.sistema_DocType as c on a.idDocType = c.id
join tgpwebged.dbo.sistema_DocType_Index as d on c.id = d.docTypeId
where d.docTypeId = 40
and (b.idIndice = 11 AND b.valor = '11111111' OR b.idIndice = 12 AND b.valor = '22222' )
)
SELECT *
FROM TempTable t1
WHERE (select count(*)
from TempTable t2
where t1.id = t2.id AND t1.valor != t2.valor) = 1
So... get all the results from your first query where there is at least one result from the table that matches on id, but does not match on valor. (This assumes you could have duplicate rows with the same valor, but you wouldn't want that.)
Try something like this. I took out the tables that didn't have direct bearing on the query, although I named them similarly, and I created a simple schema to replicate the problem. I hope this is clear, and that the connection back to your original query is likewise clear.
CREATE TABLE Documentos (ID INT, document varchar(12))
create table Indexacao (AID INT, indice int, valor varchar(12))
insert Documentos(id, document)
values (1, 'test1'),
(2, 'test2'),
(3, 'test3')
insert Indexacao (aid, indice, valor)
values (1, 11, '11111111'),
(1, 12, '22222'),
(2, 12, '22222')
The important part of the code is the INTERSECT - it returns only rows that are in both sets. In my experience this operator is usually more efficient than anything containing an OR. In the query below, we are getting only those Indexacao rows whose idDocumentos are in the INTERSECT of the two sets of conditions.
SELECT Ind.*
FROM Indexacao Ind
JOIN (
SELECT D.ID
FROM Documentos D
JOIN Indexacao I
ON D.ID = I.AID
WHERE I.Indice = 11 AND I.valor = '11111111'
INTERSECT
SELECT D.ID
FROM Documentos D
JOIN Indexacao I
ON D.ID = I.AID
WHERE I.Indice = 12 AND I.valor = '22222'
)Doc (ID)
ON Doc.ID = Ind.AID
This assumes that you don't have duplicate Indice, Valor rows for a single idDocumento - if you do, you will need to add a DISTINCT.

Categories

Resources