具有非唯一列的左联接/合并数据表c#

具有非唯一列的左联接/合并数据表c#,c#,C#,我有两个不同结构的数据表,一个表有一个列调用“活动ID”,它不是唯一的,我想在B表的“活动ID”上加入它,它是唯一的 因此,它相当于sql中的内容 select * from A left join B on A.[Campagin ID] = B.[Cmpaign ID] 我尝试了datatable.merge,但它不起作用,因为它只能基于唯一的列字段进行合并 我试过林克和拉姆达 var resultDt = from c in dt.AsEnumerable()

我有两个不同结构的数据表,一个表有一个列调用“活动ID”,它不是唯一的,我想在B表的“活动ID”上加入它,它是唯一的

因此,它相当于sql中的内容

select * from 
A left join B on 
A.[Campagin ID] = B.[Cmpaign ID]
我尝试了datatable.merge,但它不起作用,因为它只能基于唯一的列字段进行合并

我试过林克和拉姆达

var resultDt = from c in dt.AsEnumerable()
                       join lookup in lookupDt.AsEnumerable() on c["Campaign ID"].ToString() equals lookup["EventID"]
                           .ToString() into results
                       from r in results.DefaultIfEmpty()
                       select new { a=c, b =lookup };
它返回两组数据行,而不是一组数据行

我也试过使用字典,但运行起来太贵了

预期结果 如果我选择r,它将只返回表B的值

I expected the output would be like
select * from 
A left join B on 
A.[Campagin ID] = B.[Cmpaign ID]
在SQL中

如果表A是

Campaign ID                            Description      Number
eda1e64c-0002-4000-8000-000000000198            
eda1e64c-0002-4000-8000-000000000198            
eda1e64c-0002-4000-8000-000000000198            
eda1e64c-0002-4000-8000-000000000198            
eda1e64c-0002-4000-8000-000000000000    Testing 123     1111
                                        Description 2   3333
表B类似于

Campaign ID                             Name      
eda1e64c-0002-4000-8000-000000000198    Test Name1  
eda1e64c-0002-4000-8000-000000000000    Test Name2      
预期结果

Campaign ID                             Description      Number   Name
eda1e64c-0002-4000-8000-000000000198                             Test Name1
eda1e64c-0002-4000-8000-000000000198                             Test Name1
eda1e64c-0002-4000-8000-000000000198                             Test Name1
eda1e64c-0002-4000-8000-000000000198                             Test Name1
eda1e64c-0002-4000-8000-000000000000    Testing 123     1111     Test Name2
有没有什么默认的c#方法可以使用,或者有什么有效的方法可以使用?
非常感谢您的帮助。

我想您已经差不多做到了,只需将LINQ查询输出转换为一个对象数组,并将其作为一件单独的事情转换为一个新的数据表;请记住,LINQ主要用于查询和返回结果集合,而不是修改现有内容:

使用LINQ左连接,手动输出列表,手动消耗到数据表中

            var query =
                from ce in c.AsEnumerable()
                join le in lookup.AsEnumerable() on c.Field<Guid>("Campaign ID") equals le.Field<Guid>("Campaign ID") into cele
                from lenull in cele.DefaultIfEmpty()
                select new object[]
                {
                  ce.Field<Guid>("Campaign ID"),
                  ce.Field<string>("Description"),
                  ce.Field<int>("Number"), //don't know how your table has null here, maybe <int?>
                  lenull?.Field<string>("Name")
                };

            DataTable c = new DataTable(); //to hold results
            c.Columns.Add("Campaign ID", typeof(Guid)); 
            c.Columns.Add("Description"); 
            c.Columns.Add("Number", typeof(int)); 
            c.Columns.Add("Name");
            foreach (var at in query)
                c.Rows.Add(at);
与LINQ左连接,手动输出列表,动态消耗

            var query =
                from ae in a.AsEnumerable()
                join be in b.AsEnumerable() on ae.Field<int>("ID") equals be.Field<int>("ID_") into aebe
                from be2 in aebe.DefaultIfEmpty()
                select new Dictionary<string, object>
                {
                    {"ID", ae.Field<int>("ID")},
                    {"Name", ae.Field<string>("Name") },
                    {"Age", ae.Field<int>("Age") },
                    {"Address", be2?.Field<string>("Address") },
                    {"YearsAt", be2?.Field<int>("YearsAt") }
                };

            //setup datatable
            DataTable c = new DataTable();                    

            int keyCount = query.First().Keys.Count; //track columns needed to be added
            foreach (var dict in query)
            {
                var ro = c.NewRow();
                foreach (string key in dict.Keys)
                {
                    if (keyCount > 0 && dict[key] != null && !c.Columns.Contains(key))
                    { //if the column is not in the table, and the value isnt null (so we can deduce the type)
                        c.Columns.Add(key, dict[key].GetType());
                        keyCount--; //mark it as added. Eventually this will hit 0 and we won't evaluate the other two clauses
                    }

                    if (dict[key] != null) //don't store nulls
                        ro[key] = dict[key];
                }
                c.Rows.Add(ro);
            } 
             var query =
                from ae in a.AsEnumerable()
                join be in b.AsEnumerable() on ae.Field<int>("ID") equals be.Field<int>("ID_") into aebe
                from be2 in aebe.DefaultIfEmpty()
                select MapToDict(ae, be2);

            //setup datatable
            DataTable c = new DataTable();                    

            int keyCount = query.First().Keys.Count;
            foreach (var dict in query)
            {
                //have we got all our columns addded yet?
                var ro = c.NewRow();
                foreach (string key in dict.Keys)
                {
                    if (keyCount > 0 && dict[key] != null && !c.Columns.Contains(key))
                    { //if the column is not in the table, and the value isnt null (so we can deduce the type)
                        c.Columns.Add(key, dict[key].GetType());
                        keyCount--; //mark it as added. Eventually this will hit 0 and we won't evaluate the other two clauses
                    }

                    if (dict[key] != null) //don't store nulls
                        ro[key] = dict[key];
                }
                c.Rows.Add(ro);
            }
将其转换为扩展方法,以便任何表都可以像a.LeftJoin(b,aID:“ID”,bID:“ID”)一样将另一个表连接到它上,这可能是相当简单的。。如果您想要一个比简单的equals更复杂的逻辑,那么一些代码更改是必要的

出于好奇,我背靠背地尝试了所有4种方法,对它们进行计时。在我的上下文中,循环比具有固定结构和硬编码列名的LINQ快2.5倍,比使用字典使事情动态化快4倍:

        for (int lc = 0; lc < 10; lc++) {

            //setup 100K rows
            DataTable a = new DataTable();
            a.Columns.Add("ID", typeof(int));
            a.Columns.Add("Name", typeof(string));
            a.Columns.Add("Age", typeof(int));
            DataTable b = new DataTable();
            var pk = b.Columns.Add("ID", typeof(int));
            b.Columns.Add("Address", typeof(string));
            b.Columns.Add("YearsAt", typeof(int));
            b.PrimaryKey = new[] { pk };

            Random r = new Random();
            for (int i = 0; i < 100000; i++)
            {
                a.Rows.Add(i, Guid.NewGuid().ToString(), r.Next(20, 99));
                if (r.Next(0, 9) < 1)
                    b.Rows.Add(i, Guid.NewGuid().ToString(), r.Next(1, 10));

            }

            Stopwatch sw = Stopwatch.StartNew();

### INSERT CHOSEN METHOD HERE ###

            sw.Stop();

            Console.WriteLine($"Time: {sw.ElapsedMilliseconds}ms");

        }
for(int-lc=0;lc<10;lc++){
//设置100K行
DataTable a=新的DataTable();
a、 添加(“ID”,typeof(int));
a、 添加(“名称”,类型(字符串));
a、 添加(“年龄”,类型(int));
DataTable b=新的DataTable();
var pk=b.Columns.Add(“ID”,typeof(int));
b、 添加(“地址”,类型(字符串));
b、 添加(“YearsAt”,类型(int));
b、 PrimaryKey=new[]{pk};
随机r=新随机();
对于(int i=0;i<100000;i++)
{
a、 Add(i,Guid.NewGuid().ToString(),r.Next(20,99));
if(r.Next(0,9)<1)
b、 Add(i,Guid.NewGuid().ToString(),r.Next(1,10));
}
秒表sw=Stopwatch.StartNew();
###在此处插入所选方法###
sw.Stop();
WriteLine($“时间:{sw.elapsedmillyses}ms”);
}

循环处理100K行的结果通常为80ms,LINQ硬代码(手动选择,手动表格)的结果为200ms,LINQ字典(dynamic something)方法的结果为400ms。

类似于:

TableAlist.Select(A => A.CampaignId, A.Description, A.Number, 
     Name = TableBlist.FirstOrDefault(B => B.CampaignId == A.CampaignId)?.Name ?? "").ToList()

请更新以显示示例数据,以便我们可以可视化查询正在执行的操作。您当前在输出中得到了什么。因为join mysql查询在meCan看来很好,所以请将示例数据显示为有效的C代码,用于填充
DataTable
s?“它返回两组数据行”-对LINQ公平地说,当您说想要一个具有两个属性(都是datarows)的新对象时,这就是您告诉它要做的事情?你期待一个单独的数据行吗?是的,我期待一个单独的数据行,我想出来了,但因为有30多个字段,如果我不必一个接一个地键入,那就太好了,我看到人们将两个数据行添加到一个数据行中,不幸的是,这对我不起作用。这是我们长期以来一直使用的方式,然而,我们最终总是会编写/调用相当多的冗余代码,如果数据表很大,那么性能就没有那么好。非常感谢您的帮助。我以前的代码有一些问题;我已经修改了上面的答案,以提供更详细的信息。非常感谢您的回复:)
        for (int lc = 0; lc < 10; lc++) {

            //setup 100K rows
            DataTable a = new DataTable();
            a.Columns.Add("ID", typeof(int));
            a.Columns.Add("Name", typeof(string));
            a.Columns.Add("Age", typeof(int));
            DataTable b = new DataTable();
            var pk = b.Columns.Add("ID", typeof(int));
            b.Columns.Add("Address", typeof(string));
            b.Columns.Add("YearsAt", typeof(int));
            b.PrimaryKey = new[] { pk };

            Random r = new Random();
            for (int i = 0; i < 100000; i++)
            {
                a.Rows.Add(i, Guid.NewGuid().ToString(), r.Next(20, 99));
                if (r.Next(0, 9) < 1)
                    b.Rows.Add(i, Guid.NewGuid().ToString(), r.Next(1, 10));

            }

            Stopwatch sw = Stopwatch.StartNew();

### INSERT CHOSEN METHOD HERE ###

            sw.Stop();

            Console.WriteLine($"Time: {sw.ElapsedMilliseconds}ms");

        }
TableAlist.Select(A => A.CampaignId, A.Description, A.Number, 
     Name = TableBlist.FirstOrDefault(B => B.CampaignId == A.CampaignId)?.Name ?? "").ToList()