首先介绍一下背景:
我开发了一种方法,可以使用IEnumerable
和yield
从SQL中的表中批量或页检索行。当您需要只读访问权限时,它会很好用,但是当您还需要对基础数据进行更新时,它就不会那么好。
因此,我编写了一种方法,该方法采用通用的DataTable
,并建立了一条更新语句,然后将其与整个DataTable
一起作为表值参数传递给SQL。
该方法如下所示:
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添加了一堆列和值,但对于另一个UID,我可能会添加更多列或不为现有列添加值。这样做的问题是更新,因为它将用null值消除数据库中已有的任何内容,除非我明确表示“使此为null”,否则我不希望这样做。
我想通过提供一个默认值来解决此问题,然后可以在我的update语句中检查该值,然后在
CASE
语句中使用UPDATE
检查该值,并使用原始值(因此从本质上讲,我可以忽略列名称前的“ 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列的值永远不会设置,因此默认为null。当我使用更新语句时,即使表中已有内容,SQL也会将其设置为null,但是我根本不希望它更新该行的字段,因为我没有指定值为了它。
我希望能够用一个值代替
?
而不是将其默认设置为null,因此我可以将update语句更改为类似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/