【问题标题】:How to Left Outer Join two DataTables in c#?如何在c#中左外连接两个数据表?
【发布时间】:2013-07-16 18:38:19
【问题描述】:

如何在保留两个表中的所有列的同时将两个数据表与以下表和条件进行左外连接?

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      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 中不存在的外键,则将插入空或空字段。
  • 加入 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:

这与我想要的很接近,但它只有一张表中的列(在此 link 找到):

    dtblJoined = (from t1 in dtblA.Rows.Cast<DataRow>()
                  join t2 in dtblB.Rows.Cast<DataRow>() on t1["col1"] equals t2["col1"]
                  select t1).CopyToDataTable();

编辑 2:

来自link 的答案似乎对我有用,但我不得不对其进行一些更改,如下所示:

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);

我还发现了这个link,我可能会尝试一下,因为它看起来更简洁。

编辑 3(2013 年 11 月 18 日):

更新了表格以反映更多情况。

【问题讨论】:

  • 这也是自然连接。告诉我们当 dtblA 有 '4' 而 dtblB 没有时应该发生什么。
  • 以防万一你错过了关于同一问题的其他帖子Check hereand here
  • 更新为显示 dtblA 为 4
  • "left OUTER join" 表示在 dtblLeft 中显示所有 col1 条目,这些条目在 dtblRight 中的 col1 中不存在。您可以阅读“OUTER JOIN”,例如“不在加入/匹配另一个表的条目组中”。

标签: c# linq join datatable


【解决方案1】:

感谢大家的帮助。以下是我根据多种资源得出的结论:

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:

我有一个特定的场景让我意识到以前的版本实际上是在做一个内部连接。该功能已被修改以解决该问题。我使用link 的信息来解决这个问题。

【讨论】:

  • @adam-c 此代码已经从两个表中获取了唯一列。
【解决方案2】:

这只是两个表之间的内部连接:

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();

生产:

col1 col2
1    Hi 
1    Hi 
2    Bye 
3    Later 
3    Later 
3    Later 

【讨论】:

    【解决方案3】:

    您可能可以使用 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,但它应该让你更接近你正在寻找的我认为。不过可能需要稍作调整。

    【讨论】:

      猜你喜欢
      • 2015-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-09
      • 1970-01-01
      相关资源
      最近更新 更多