C# 如何在c中左外连接两个数据表?
我认为这是左外连接,但我不能100%确定两个数据表都有以下表和条件,同时保留两个表中的所有列 dtblLeft:C# 如何在c中左外连接两个数据表?,c#,linq,join,datatable,C#,Linq,Join,Datatable,我认为这是左外连接,但我不能100%确定两个数据表都有以下表和条件,同时保留两个表中的所有列 dtblLeft: id col1 anotherColumn2 1 1 any2 2 1 any2 3 2 any2 4 3 any2 5 3 any2 6 3 any2 7 any2 dtblRight: col1 col2 anotherCo
id col1 anotherColumn2
1 1 any2
2 1 any2
3 2 any2
4 3 any2
5 3 any2
6 3 any2
7 any2
dtblRight:
col1 col2 anotherColumn1
1 Hi any1
2 Bye any1
3 Later any1
4 Never any1
DTBL已加入:
id col1 col2 anotherColumn1 anotherColumn2
1 1 Hi any1 any2
2 1 Hi any1 any2
3 2 Bye any1 any2
4 3 Later any1 any2
5 3 Later any1 any2
6 3 Later any1 any2
7 any2
条件:
在dtblLeft中,col1不需要具有唯一值。
在dtblRight中,col1具有唯一的值。
如果dtblLeft在col1中缺少外键,或者它有一个在dtblRight中不存在的外键,则将插入空字段或null字段。
加入col1。
我可以使用常规的DataTable操作、LINQ或任何东西
我尝试了这个,但它删除了重复项:
dtblA.PrimaryKey = new DataColumn[] {dtblA.Columns["col1"]}
DataTable dtblJoined = new DataTable();
dtblJoined.Merge(dtblA, false, MissingSchemaAction.AddWithKey);
dtblJoined.Merge(dtblB, false, MissingSchemaAction.AddWithKey);
编辑1:
这与我想要的很接近,但它只包含位于以下位置的其中一个表中的列:
编辑2:
这个问题的答案似乎对我有用,但我不得不改变一下,如下所示:
DataTable targetTable = dtblA.Clone();
var dt2Columns = dtblB.Columns.OfType<DataColumn>().Select(dc =>
new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping));
var dt2FinalColumns = from dc in dt2Columns.AsEnumerable()
where targetTable.Columns.Contains(dc.ColumnName) == false
select dc;
targetTable.Columns.AddRange(dt2FinalColumns.ToArray());
var rowData = from row1 in dtblA.AsEnumerable()
join row2 in dtblB.AsEnumerable()
on row1["col1"] equals row2["col1"]
select row1.ItemArray.Concat(row2.ItemArray.Where(r2 => row1.ItemArray.Contains(r2) == false)).ToArray();
foreach (object[] values in rowData)
targetTable.Rows.Add(values);
我还发现了这个,我可能会尝试一下,因为它看起来更简洁
编辑3 2013年11月18日:
更新表以反映更多情况。您可能可以使用LINQ并执行以下操作:
var dtblJoined = from dB in dtblB.AsEnumerable()
join dA in dtblA.AsEnumerable() on dA.col1 equals dB.col1 into dAB
from d in dAB.DefaultIfEmpty()
select new (col1 = dB.col1, ; col2 = (dB.col1 == dA.col1) ? dA.col2 : null);
这将返回一个IEnumerable作为结果,而不是一个DataTable,但我认为它应该让您更接近您所寻找的内容。不过可能需要稍微调整一下 这只是两个表之间的内部联接:
var query = (from x in a.AsEnumerable()
join y in b.AsEnumerable() on x.Field<int>("col1") equals y.Field<int>("col1")
select new { col1= y.Field<int>("col1"), col2=x.Field<int>("col2") }).ToList();
谢谢大家的帮助。以下是我根据多种资源得出的结论:
public static class DataTableHelper
{
public enum JoinType
{
/// <summary>
/// Same as regular join. Inner join produces only the set of records that match in both Table A and Table B.
/// </summary>
Inner = 0,
/// <summary>
/// Same as Left Outer join. Left outer join produces a complete set of records from Table A, with the matching records (where available) in Table B. If there is no match, the right side will contain null.
/// </summary>
Left = 1
}
/// <summary>
/// Joins the passed in DataTables on the colToJoinOn.
/// <para>Returns an appropriate DataTable with zero rows if the colToJoinOn does not exist in both tables.</para>
/// </summary>
/// <param name="dtblLeft"></param>
/// <param name="dtblRight"></param>
/// <param name="colToJoinOn"></param>
/// <param name="joinType"></param>
/// <returns></returns>
/// <remarks>
/// <para>http://stackoverflow.com/questions/2379747/create-combined-datatable-from-two-datatables-joined-with-linq-c-sharp?rq=1</para>
/// <para>http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx</para>
/// <para>http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html</para>
/// <para>http://stackoverflow.com/questions/406294/left-join-and-left-outer-join-in-sql-server</para>
/// </remarks>
public static DataTable JoinTwoDataTablesOnOneColumn(DataTable dtblLeft, DataTable dtblRight, string colToJoinOn, JoinType joinType)
{
//Change column name to a temp name so the LINQ for getting row data will work properly.
string strTempColName = colToJoinOn + "_2";
if (dtblRight.Columns.Contains(colToJoinOn))
dtblRight.Columns[colToJoinOn].ColumnName = strTempColName;
//Get columns from dtblLeft
DataTable dtblResult = dtblLeft.Clone();
//Get columns from dtblRight
var dt2Columns = dtblRight.Columns.OfType<DataColumn>().Select(dc => new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping));
//Get columns from dtblRight that are not in dtblLeft
var dt2FinalColumns = from dc in dt2Columns.AsEnumerable()
where !dtblResult.Columns.Contains(dc.ColumnName)
select dc;
//Add the rest of the columns to dtblResult
dtblResult.Columns.AddRange(dt2FinalColumns.ToArray());
//No reason to continue if the colToJoinOn does not exist in both DataTables.
if (!dtblLeft.Columns.Contains(colToJoinOn) || (!dtblRight.Columns.Contains(colToJoinOn) && !dtblRight.Columns.Contains(strTempColName)))
{
if (!dtblResult.Columns.Contains(colToJoinOn))
dtblResult.Columns.Add(colToJoinOn);
return dtblResult;
}
switch (joinType)
{
default:
case JoinType.Inner:
#region Inner
//get row data
//To use the DataTable.AsEnumerable() extension method you need to add a reference to the System.Data.DataSetExtension assembly in your project.
var rowDataLeftInner = from rowLeft in dtblLeft.AsEnumerable()
join rowRight in dtblRight.AsEnumerable() on rowLeft[colToJoinOn] equals rowRight[strTempColName]
select rowLeft.ItemArray.Concat(rowRight.ItemArray).ToArray();
//Add row data to dtblResult
foreach (object[] values in rowDataLeftInner)
dtblResult.Rows.Add(values);
#endregion
break;
case JoinType.Left:
#region Left
var rowDataLeftOuter = from rowLeft in dtblLeft.AsEnumerable()
join rowRight in dtblRight.AsEnumerable() on rowLeft[colToJoinOn] equals rowRight[strTempColName] into gj
from subRight in gj.DefaultIfEmpty()
select rowLeft.ItemArray.Concat((subRight== null) ? (dtblRight.NewRow().ItemArray) :subRight.ItemArray).ToArray();
//Add row data to dtblResult
foreach (object[] values in rowDataLeftOuter)
dtblResult.Rows.Add(values);
#endregion
break;
}
//Change column name back to original
dtblRight.Columns[strTempColName].ColumnName = colToJoinOn;
//Remove extra column from result
dtblResult.Columns.Remove(strTempColName);
return dtblResult;
}
}
编辑3:
该方法现在可以正常工作,并且在表有2000多行时仍然很快。如有任何建议/建议/改进,将不胜感激
编辑4:
我有一个特定的场景,使我意识到前一个版本实际上是在进行内部连接。已修改该函数以修复该问题。我在这里使用了信息来解决这个问题。这也是自然加入。请告诉我们当dtblA有“4”而dtblB没有时应该发生什么。以防您错过了SO上关于同一问题的其他帖子,已更新以显示dtblA4@adam-c此代码已从两个表中获取唯一的列。
col1 col2
1 Hi
1 Hi
2 Bye
3 Later
3 Later
3 Later
public static class DataTableHelper
{
public enum JoinType
{
/// <summary>
/// Same as regular join. Inner join produces only the set of records that match in both Table A and Table B.
/// </summary>
Inner = 0,
/// <summary>
/// Same as Left Outer join. Left outer join produces a complete set of records from Table A, with the matching records (where available) in Table B. If there is no match, the right side will contain null.
/// </summary>
Left = 1
}
/// <summary>
/// Joins the passed in DataTables on the colToJoinOn.
/// <para>Returns an appropriate DataTable with zero rows if the colToJoinOn does not exist in both tables.</para>
/// </summary>
/// <param name="dtblLeft"></param>
/// <param name="dtblRight"></param>
/// <param name="colToJoinOn"></param>
/// <param name="joinType"></param>
/// <returns></returns>
/// <remarks>
/// <para>http://stackoverflow.com/questions/2379747/create-combined-datatable-from-two-datatables-joined-with-linq-c-sharp?rq=1</para>
/// <para>http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx</para>
/// <para>http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html</para>
/// <para>http://stackoverflow.com/questions/406294/left-join-and-left-outer-join-in-sql-server</para>
/// </remarks>
public static DataTable JoinTwoDataTablesOnOneColumn(DataTable dtblLeft, DataTable dtblRight, string colToJoinOn, JoinType joinType)
{
//Change column name to a temp name so the LINQ for getting row data will work properly.
string strTempColName = colToJoinOn + "_2";
if (dtblRight.Columns.Contains(colToJoinOn))
dtblRight.Columns[colToJoinOn].ColumnName = strTempColName;
//Get columns from dtblLeft
DataTable dtblResult = dtblLeft.Clone();
//Get columns from dtblRight
var dt2Columns = dtblRight.Columns.OfType<DataColumn>().Select(dc => new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping));
//Get columns from dtblRight that are not in dtblLeft
var dt2FinalColumns = from dc in dt2Columns.AsEnumerable()
where !dtblResult.Columns.Contains(dc.ColumnName)
select dc;
//Add the rest of the columns to dtblResult
dtblResult.Columns.AddRange(dt2FinalColumns.ToArray());
//No reason to continue if the colToJoinOn does not exist in both DataTables.
if (!dtblLeft.Columns.Contains(colToJoinOn) || (!dtblRight.Columns.Contains(colToJoinOn) && !dtblRight.Columns.Contains(strTempColName)))
{
if (!dtblResult.Columns.Contains(colToJoinOn))
dtblResult.Columns.Add(colToJoinOn);
return dtblResult;
}
switch (joinType)
{
default:
case JoinType.Inner:
#region Inner
//get row data
//To use the DataTable.AsEnumerable() extension method you need to add a reference to the System.Data.DataSetExtension assembly in your project.
var rowDataLeftInner = from rowLeft in dtblLeft.AsEnumerable()
join rowRight in dtblRight.AsEnumerable() on rowLeft[colToJoinOn] equals rowRight[strTempColName]
select rowLeft.ItemArray.Concat(rowRight.ItemArray).ToArray();
//Add row data to dtblResult
foreach (object[] values in rowDataLeftInner)
dtblResult.Rows.Add(values);
#endregion
break;
case JoinType.Left:
#region Left
var rowDataLeftOuter = from rowLeft in dtblLeft.AsEnumerable()
join rowRight in dtblRight.AsEnumerable() on rowLeft[colToJoinOn] equals rowRight[strTempColName] into gj
from subRight in gj.DefaultIfEmpty()
select rowLeft.ItemArray.Concat((subRight== null) ? (dtblRight.NewRow().ItemArray) :subRight.ItemArray).ToArray();
//Add row data to dtblResult
foreach (object[] values in rowDataLeftOuter)
dtblResult.Rows.Add(values);
#endregion
break;
}
//Change column name back to original
dtblRight.Columns[strTempColName].ColumnName = colToJoinOn;
//Remove extra column from result
dtblResult.Columns.Remove(strTempColName);
return dtblResult;
}
}