c# - 我可以在 SQL 中使用什么作为内部标识符?

标签 c# sql-server

首先介绍一下背景:

我开发了一种通过使用 IEnumerableyield 从 SQL 表中批量或分页检索行的方法。当您需要只读访问权限时它非常有用,但当您还需要对基础数据进行更新时效果不佳。

所以我写了一个接受通用DataTable的方法,并构建了一个更新语句,然后将其作为一个表值传递给SQL以及整个DataTable参数。

方法如下所示:

string[] validColumns = SQL_Columns.Split(',');
    foreach(DataColumn column in p_UpdatesTable.Columns)
    {
      if(!validColumns.Contains(column.ColumnName))
      {
        throw new Exception("Column '" + column.ColumnName + "' is not valid for this table");
      }
    }

    //Establish SQL Connection
    using (SqlConnection sqlConnection = new SqlConnection(connectionString))
    {
      sqlConnection.Open();
      StringBuilder commandBuilder = new StringBuilder();
      commandBuilder.Append("UPDATE Table SET ");
      List<string> columnsToUpdate = new List<string>(p_UpdatesTable.Columns.Count);
      foreach(DataColumn column in p_UpdatesTable.Columns)
      {
        if (!column.ColumnName.Equals("UID", StringComparison.InvariantCultureIgnoreCase))
        {
          StringBuilder columnBuilder = new StringBuilder();
          columnBuilder.Append(column.ColumnName);
          columnBuilder.Append(" = U.");
          columnBuilder.Append(column.ColumnName);
          columnsToUpdate.Add(columnBuilder.ToString());
        }
      }
      commandBuilder.Append(string.Join(",", columnsToUpdate.ToArray()));
      commandBuilder.Append(" FROM @UpdateTable AS U WHERE UID = U.UID");
      using (SqlCommand sqlCommand = new SqlCommand(commandBuilder.ToString(), sqlConnection))
      {
        SqlParameter updateTableParameter = sqlCommand.Parameters.Add("UpdateTable", SqlDbType.Structured);
        updateTableParameter.Value = p_UpdatesTable;
        int rowsAffected = sqlCommand.ExecuteNonQuery();
        if(rowsAffected != p_UpdatesTable.Rows.Count)
        {
          throw new Exception("Update command affected " + rowsAffected + " rows out of the " + p_UpdatesTable.Rows.Count + " expected.");
        }
      }
      sqlConnection.Dispose();
    }

然后我构建了这个方法来填充更新表:

private void AddUpdate(ref DataTable p_UpdateTable, string p_ColumnName, long p_uid, object p_value)
{
  if(!StronglyTypedDataset.Columns.Contains(p_ColumnName))
  {
    throw new ArgumentException("Table '" + p_ColumnName + "' does not exist in table", "p_ColumnName");
  }
  if(!p_UpdateTable.Columns.Contains(p_ColumnName))
  {
    DataColumn columnToAdd = p_UpdateTable.Columns.Add(p_ColumnName);
    columnToAdd.DataType = StronglyTypedDataset.Columns.Cast<DataColumn>().Where(c => c.ColumnName.Equals(p_ColumnName)).First().DataType;
  }
  var existingRow = p_UpdateTable.Rows.Cast<DataRow>().Where(r => Convert.ToInt64(r["UID"]) == p_uid).FirstOrDefault();
  if(existingRow != null)
  {
    existingRow[p_ColumnName] = p_value;
  }
  else
  {
    DataRow newRow = p_UpdateTable.NewRow();
    newRow["UID"] = p_uid;
    newRow[p_ColumnName] = p_value;
    p_UpdateTable.Rows.Add(newRow);
  }
}

有几次我需要调用它,所以这是一个比其他任何方法都更方便的方法。

现在的问题是:我可能会为一个 UID 添加一堆列和值,但对于另一个,我可能会添加更多列或不为现有列添加值。这个问题是更新,因为它会用空值抹掉数据库中已经存在的任何内容,我不希望这样,除非我明确地说“使它为空”。

我正在考虑通过提供一个默认值来解决这个问题,然后我可以在更新语句中检查它,然后在 UPDATE 中使用 CASE检查此值并使用原始值的语句(因此本质上我可以忽略列名前的“U.”)。问题是该表是通用的,所以里面可能有任何东西,如果实际数据以某种方式与我的默认值匹配,事情就会崩溃。

需要注意的是,这个更新表会建成一个批处理,一次批处理更新,而不是逐行更新。

是否有保证不被使用的值,也许是 GUID(我知道仍然可能发生冲突)或类似的东西?

一个例子:

假设我的表格在一行之后看起来像这样:

      |  UID   | column 1 | column 2 |
row 1 |    1   |   x      |    y     |

第二行看起来像这样:

      |  UID   | column 1 | column 2 | column 3 |
row 1 |    1   |   x      |    y     |    ?     |
row 2 |    2   |   x      |    y     |    z     |

第 1 行第 3 列的值永远不会设置,因此默认为空。当我使用更新语句时,即使表中已有内容,SQL 也会将该值设置为 null,但我根本不希望它更新该行的字段,因为我没有指定值

我希望能够用一个值代替 ? 而不是默认为 null,这样我就可以将更新语句更改为类似 UPDATE Table SET Column1 = U.Column1,Column2 = U.Column2,Column3 = CASE WHEN U.Column3 = somevalue THEN Column3 ELSE U.Column3 END FROM @UpdateTable U

最佳答案

你可以让自己一点点 Maybe<T>这可以是 T 的实际值,在这种情况下,您将推送更新,或者它可能是一个特殊的非值。它可能看起来像这样:

public sealed class Maybe<T> {
    private readonly T value;
    private readonly bool hasValue;

    private Maybe() {
        hasValue = false;
    }

    public readonly Maybe<T> Nothing = new Maybe();

    public Maybe(T value) {
        this.value = value;
        hasValue = true;
    }

    public T Value {
        get {
            return value;
        }
    }

    public bool HasValue {
        get {
            return value;
        }
    }
}

你可以这样使用:

private void AddUpdate<T>(DataTable p_UpdateTable, string p_ColumnName, long p_uid, Maybe<T> p_value) {
    // ...
    if(existingRow != null) {
        if(p_value.HasValue)
            existingRow[p_ColumnName] = p_value.Value;
    }
    else {
        DataRow newRow = p_UpdateTable.NewRow();
        newRow["UID"] = p_uid;
        if(p_value.HasValue)
            newRow[p_ColumnName] = p_value.Value;
        p_UpdateTable.Rows.Add(newRow);
    }
    // ...
}

您不需要 ref对于 DataTable参数,顺便说一下。

关于c# - 我可以在 SQL 中使用什么作为内部标识符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34338792/

相关文章:

c# - 使用动态列名称更新 Dapper

c# - 反射中的 .NET Framework 程序集

sql-server - 如何将 IBM DB2 服务器添加到 SQL Server 的链接服务器

sql-server - 在大型数据集中查找给定查找日期的最后一次已知事件

sql - 在 SQL Server 中按多列分组

sql-server - SQL 服务器 2016 : How to remove records that are NOT duplicates but have duplicate values?

c# - 通过 using block 使用进程

c# - 如何正确连接MySQL数据库与C#应用程序?

c# - C# 中的接口(interface)委托(delegate)

sql - INSERT INTO .. SELECT .. 违反唯一约束