简体   繁体   中英

C# Linq query which joins two tables in to one?

I need help with my code. I have two data tables which have the same structure: columns and column types. The difference is that some of the table cells can be empty or filled. The issue is that I need to join them in to one and replace the null values with existing values if they will appear in one of the tables, without creating duplicate records.

Structure dt1 EmployeeID|Company|Driver name|Manager|

Structure dt2 EmployeeID|Company|Driver name|Manager|

Example data:

Structure dt1

EmployeeID|Company|Driver name|Manager  |Manager Email     |HR Admin
000000001 |VW     |John       |Veronica |null              |Lucas
000000002 |Audi   |Monica     |John     |john@john.com     |null
000000003 |Fiat   |Thomas     |Michael  |null              |null

Structure dt1

EmployeeID|Company|Driver name|Manager  |Manager Email     |HR Admin
000000001 |VW     |John       |null     |Veronica@john.com |null
000000002 |null   |Monica     |John     |null              |Martha
000000003 |null   |null       |Michael  |Michael@john.com  |Lucas

Expected result should be:

EmployeeID|Company|Driver name|Manager  |Manager Email     |HR Admin
000000001 |VW     |John       |Veronica |Veronica@john.com |Lucas
000000002 |Audi   |Monica     |John     |john@john.com     |Martha
000000003 |Fiat   |Thomas     |Michael  |Michael@john.com  |Lucas

I have searched on google and came with this code but it gives back wrong result. Im not familiar with LINQ. Thank you for your support!

try
            {

ErrorMessage = "";
                CollectionOut = new DataTable();
                DataTable dt1 = new DataTable();
                DataTable dt2 = new DataTable();
                            DataTable dt3 = new DataTable();
                            dt3.Columns.Add("EmployeeID", typeof(string));
                            dt3.Columns.Add("Company", typeof(string));
                            dt3.Columns.Add("Driver name", typeof(string));
                            dt3.Columns.Add("Manager", typeof(string));
                            dt3.Columns.Add("Manager Email", typeof(string));
                            dt3.Columns.Add("HR Admin", typeof(string));

                dt3 = (from a in dt1.AsEnumerable()
                       join b in dt2.AsEnumerable() on a.Field<string>("EmployeeID") equals b.Field<string>("EmployeeID")
                       select dt3.LoadDataRow(new object[]
                       {
                            a.Field<string>("EmployeeID"),
                            b.Field<string>("Company"),
                            b.Field<string>("Driver name"),
                            b.Field<string>("Manager"),
                            b.Field<string>("Manager Email"),
                            b.Field<string>("HR Admin"),
                       }, false))
                    .CopyToDataTable();
CollectionOut = dt3;
            }
                catch (Exception ex)
            {
                ErrorMessage = ex.Message.ToString();

            }

Your code is almost do what you want. You only need to use

a.Field<string>("EmployeeID"),
a.Field<string>("Company") ?? b.Field<string>("Company"),
a.Field<string>("Driver name") ?? b.Field<string>("Driver name"),
a.Field<string>("Manager") ?? b.Field<string>("Manager"),
a.Field<string>("Manager Email") ?? b.Field<string>("Manager Email"),
a.Field<string>("HR Admin") ?? b.Field<string>("HR Admin"),

instead of

a.Field<string>("EmployeeID"),
b.Field<string>("Company"),
b.Field<string>("Driver name"),
b.Field<string>("Manager"),
b.Field<string>("Manager Email"),
b.Field<string>("HR Admin"),

In your example you set EmployeeID from first table and all other columns from second table into result table. But if you want to fill result table excluding nulls, then you have to try to set values from first table and if they are null, then set values from second tables. (For more info you can read about ?? operator

You also can fill your result table from any source table as you choose and you even can use different columns as a source for 1 column in result table. Here is an example.

        var ErrorMessage = "";
        try
        {
            var CollectionOut = new DataTable();

            //Structure of first datatable
            DataTable dt1 = new DataTable();
            dt1.Columns.Add("EmployeeID", typeof(string));
            dt1.Columns.Add("Company", typeof(string));
            //dt1.Columns.Add("Driver name", typeof(string));
            //dt1.Columns.Add("Manager", typeof(string));
            dt1.Columns.Add("Manager Email", typeof(string));
            dt1.Columns.Add("HR Admin Name1", typeof(string));

            //Structure of second databable
            DataTable dt2 = new DataTable();
            dt2.Columns.Add("EmployeeID", typeof(string));
            dt2.Columns.Add("Company", typeof(string));
            dt2.Columns.Add("Driver name", typeof(string));
            dt2.Columns.Add("Manager", typeof(string));
            //dt2.Columns.Add("Manager Email", typeof(string));
            dt2.Columns.Add("HR Admin Name2", typeof(string));

            //Structure of result datatable
            DataTable dt3 = new DataTable();
            dt3.Columns.Add("EmployeeID", typeof(string));
            dt3.Columns.Add("Company", typeof(string));
            dt3.Columns.Add("Driver name", typeof(string));
            dt3.Columns.Add("Manager", typeof(string));
            dt3.Columns.Add("Manager Email", typeof(string));
            dt3.Columns.Add("HR Admin", typeof(string));

            //Data for example
            dt1.Rows.Add("1", "1", "1", "1", "1", "1");
            dt1.Rows.Add("2", "1", "1", null, "1", "1");
            dt1.Rows.Add("3", "1", "1", "1", null, "1");
            dt2.Rows.Add("1", "1", "1", null, "1", null);
            dt2.Rows.Add("2", "1", "1", "2", "1", "1");
            dt2.Rows.Add("3", "1", "1", "1", "2", "1");

            //Fill result table
            dt3 = (from a in dt1.AsEnumerable()
                         join b in dt2.AsEnumerable() on a.Field<string>("EmployeeID") equals b.Field<string>("EmployeeID") //you join table on your EmployerID here
                         select dt3.LoadDataRow(new object[]
                         {
                             a.Field<string>("EmployeeID"), //result gets EmployeeID from first table
                             a.Field<string>("Company") ?? b.Field<string>("Company"),  //result gets Company from first table, but if it is null then it gets Company from second table
                             b.Field<string>("Driver name"), //result gets Driver name from second table (because we don't have Driver name in first table
                             a.Field<string>("Manager") ?? b.Field<string>("Manager"), //same
                             a.Field<string>("Manager Email"), //result gets Manager Email from first table (because we don't have Manager Email in second table)
                             a.Field<string>("HR Admin Name1") ?? b.Field<string>("HR Admin Name2"), //fil HR Admin column from HR Admin Name1 in 1st table or, if null, from HR Admin Name2 in 2nd table
                         }, false))
                    .CopyToDataTable();
            CollectionOut = dt3;
        }
        catch (Exception ex)
        {
            ErrorMessage = ex.Message.ToString();
        }

If there are records in dt1 that are not in dt2 (Or vice versa) one join is not enough. U must do something like full outer join. I write example with arrays but u can easily replace them to DataTable

Example:

\\Helper test class

public class Test
{
   public int Id { get; set; }
   public string A { get; set; }
   public string B { get; set; }
}
var a = new[]
{
    new Test{ Id = 1, A = "AAAAA", B = null },
    new Test{ Id = 2, A = null , B = "BBBB"}
};
var b = new[]
{
    new Test{ Id = 1,  A = null , B = "BBBB" },
    new Test{ Id = 2, A = "AAAAA", B = null },
    new Test{ Id = 3, A = "AAAAA", B = "BBBBB" },
};
var leftOuterJoin =
    from first in a
    join last in b on first.Id equals last.Id into temp
    from last in temp.DefaultIfEmpty()
    select new
    {
        first.Id,
        A = !string.IsNullOrEmpty(first.A) ? first.A : last?.A,
        B = !string.IsNullOrEmpty(first.B) ? first.B : last?.B,
    };

var rightOuterJoin =
    from last in b
    join first in a on last.Id equals first.Id into temp
    from first in temp.DefaultIfEmpty()
    select new
    {
        last.Id,
        A = !string.IsNullOrEmpty(last.A) ? last.A : first?.A,
        B = !string.IsNullOrEmpty(last.B) ? last.B : first?.B,
    };

//fullOuterJoin is result
var fullOuterJoin = leftOuterJoin.Union(rightOuterJoin);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM