【问题标题】:C# Dynamic full outer join on 2 datatables2 个数据表上的 C# 动态完全外连接
【发布时间】:2018-03-04 12:25:00
【问题描述】:

我需要对 2 个数据表进行完全外部联接,我不知道数据表中有哪些列,但它们在每个表中都是相同的,我也只有我需要做的列的名称变量中的连接。有没有办法做到这一点?

我需要做的是在 C# 脚本中加入 2 个数据表。我在 SSIS 中使用数据流从几个文件中获取数据,最后我需要比较最后两组数据。我需要在任何 2 个数据表上执行此操作,只要它们具有相同的列,因此我无法在 SSIS 中完成该过程,因为我需要指定列。

GetData() 我只是在需要比较 2 个表时使用它,但 donnesPRODdonnesDEV 是从 SSIS 中的对象变量填充的。

到目前为止,这是我的代码:

DataTable donnesPROD = GetData(connectionPROD, sql_request);
DataTable donnesDEV = GetData(connectionDEV, sql_request);

这是 GetData 的代码:

DataTable GetData(string cs, string query)
        {
            OleDbConnection conn = new OleDbConnection(cs);
            conn.Open();
            OleDbCommand cmd = new OleDbCommand(query, conn);

            DataTable dt = new DataTable();
            dt.Load(cmd.ExecuteReader());
            conn.Close();
            return dt;
        }

我有另一个数据表中的列列表,并且我在字符串变量key 中有主键的名称。从这里我需要能够在key 上完成donnesPRODdonnesDEV 的完全外部连接。可以这样做吗?或者有没有办法自己动态生成脚本代码然后执行?

【问题讨论】:

标签: c# ssis full-outer-join


【解决方案1】:

你有两个选择。

条件连接

如果你不知道具体的列名,但你知道列名可能是什么,你可以像这样进行条件连接:

CREATE PROCEDURE ExampleDynamicJoin(@JoinColumn AS VarChar(40))
AS
BEGIN
    SELECT *
    FROM   TableA
    JOIN   TableB ON (@JoinColumn = 'ColumnA' AND TableA.ColumnA = TableB.ColumnA)
                  OR (@JoinColumn = 'ColumnB' AND TableA.ColumnB = TableB.ColumnB)
                  OR (@JoinColumn = 'ColumnC' AND TableA.ColumnC = TableB.ColumnC)
END

您可能无法从中获得最佳性能(条件连接会使查询引擎感到困惑,并且它可能不会选择最佳索引,如果它完全选择了一个)。如果桌子很大,你也可以这样做。看起来有点痛苦,但会获得更好的性能:

CREATE PROCEDURE ExampleDynamicJoin(@JoinColumn AS VarChar(40))
AS
BEGIN
    IF (@JoinColumn = 'ColumnA') BEGIN
        SELECT *
        FROM   TableA
        JOIN   TableB ON TableA.ColumnA = TableB.ColumnA
    END
    IF (@JoinColumn = 'ColumnB') BEGIN
        SELECT *
        FROM   TableA
        JOIN   TableB ON TableA.ColumnB = TableB.ColumnB
    END
    IF (@JoinColumn = 'ColumnC') BEGIN
        SELECT *
        FROM   TableA
        JOIN   TableB ON TableA.ColumnC = TableB.ColumnC
    END
END

如果 TableATableA 是更大查询的一部分,并且您最终会复制大量 SQL,则始终可以将仅 TableATableB 的结果集提取到临时表中,然后在更大的查询中使用临时表。

动态 SQL

如果您对列名没有最模糊的了解,并且有很多可能性,您可以将 SQL 构造为字符串并以这种方式连接。您应该验证传入的列名;这不仅会确保该列确实存在,而且当@JoinColumn 包含注入攻击时,它会阻止动态SQL 的构建,因为合法的列名不包含SQL 语句。示例:

CREATE PROCEDURE ExampleDynamicJoin(@JoinColumn AS VarChar(40))
AS
BEGIN
    DECLARE @Sql AS VarChar(MAX)

    IF NOT EXISTS 
    (
        SELECT 0 
        FROM syscolumns c 
        JOIN sysobjects o ON o.id = c.id
        WHERE  o.Name = 'TableA'
        AND    c.Name = @JoinColumn
    )
    RAISERROR  (15600,-1,-1, 'ExampleDynamicJoin');  //Throw error if column doesn't exist

    SET @Sql = 
       'SELECT *
        FROM   TableA
        JOIN   TableB ON TableA.' + @JoinColumn + ' = TableB.' + @JoinColumn

    sp_ExecuteSql @Sql
END

或者,如果你不使用存储过程,

DataTable ExampleDynamicJoin(string joinColumn)
{
    if (!ValidateColumn(joinColumn)) throw new ArgumentException();

    var sql = String.Format(
      @"SELECT * 
        FROM TableA 
        JOIN TableB ON TableA.{0} = TableB.{0}", 
        joinColumn
    );

    using (var connection = GetConnectionFromSomewhere())
    {
        using (var cmd = new SqlCommand
        {   
            CommandText = sql,
            CommandType = CommandType.Text,
            Connection = connection
        })
        {
            var reader = cmd.ExecuteReader();
            var table = new DataTable();
            table.Load(reader);
            return table;
        }
    }
}

使用动态 SQL 时,应尽可能使用参数。但是您不能将参数用作列名,因此在这种情况下您必须进行连接。连接时,始终将输入列入白名单。这就是为什么我包含一个名为 ValidateColumn 的函数,它看起来像这样:

bool ValidateColumn(string columnName)
{
    switch columnName.ToUpper()
    {
        case "COLUMNA":
        case "COLUMNB":
        case "COLUMNC":
            return true;
        default:
            return false;
    }
}

【讨论】:

  • 条件连接解决方​​案只适用于几乎很小的表,因为连接条件不是 SARGable,因此总是会导致全表或索引扫描。
  • 感谢 John Wu 提供的所有示例。我将更新问题,但我需要做的是在 C# 脚本中加入 2 个数据表。我在 SSIS 中使用数据流从几个文件中获取数据,最后我需要比较最后两组数据。只要它们具有相同的列,我就需要在任何 2 个数据表上执行此操作,因此我无法在 SSIS 中完成该过程,因为我需要指定列。
猜你喜欢
  • 1970-01-01
  • 2014-09-14
  • 2013-05-24
  • 2016-06-20
  • 2018-05-24
  • 2020-06-10
  • 2020-03-30
  • 2012-09-27
  • 1970-01-01
相关资源
最近更新 更多