【问题标题】:Select all columns except the first column for any given SQL Server table选择任何给定 SQL Server 表的除第一列之外的所有列
【发布时间】:2017-12-18 17:12:28
【问题描述】:

我在 C# 中有这段代码,但我需要它来选择除表的第一列(标识列)之外的所有列,以便当我将数据插入到不同数据库中的相同表中时,目标数据库分配自己的标识列值:

SqlCommand commandSourceData = new SqlCommand($"SELECT * FROM dbo.{tableName};", sourceConnection);
SqlDataReader reader = commandSourceData.ExecuteReader();

有没有办法做到这一点?

【问题讨论】:

  • 是的,在查询中,您可以指定所需列的名称,而不是使用* 通配符,例如select col1,col2,col3...
  • 它需要是动态的,以便我可以将任何表名传递给它,它会为该表执行此操作
  • 当您的身份列不是第一个时会发生什么?你真的应该明确地管理数据。但是,如果您对像这样的通用 catch all insert 死机,您将不得不使用动态 sql 并仅选择那些不是标识且不是计算列的列。
  • 这真的非常危险,因为您的身份列不仅不能像肖恩所说的那样是“第一”,而且您可以拥有没有身份列、加密列的表,并且您可以拥有其他列旨在直接填充(审计列、newsequentialid、临时表、rowversion、计算列等)。
  • 您能解释一下为什么需要更改这些标识值吗?您可能会破坏这些表之间存在的所有关系。

标签: c# sql-server sqlbulkcopy


【解决方案1】:

如果您想为数据库中的每一列提供通用解决方案,您可以使用这种代码

public string GetColumnsWithoutIdentity(string tableName, SqlConnection con)
{
    SqlDataAdapter da = new SqlDataAdapter($"SELECT * FROM dbo.{tableName} where 1=0", con);
    DataTable dt = new DataTable();
    da.FillSchema(dt, SchemaType.Source);
    var cols = dt.Columns.Cast<DataColumn>().Where(x => !x.AutoIncrement).Select(x => x.ColumnName);
    return string.Join(",", cols);
}

现在您可以使用返回的字符串来构建没有自动增量列的 Sql 语句。
请注意,此代码易受 Sql Injection 攻击。您应该绝对确定用于构建第一个查询的 tableName 参数不是由您的用户直接键入的。让它从预定义表的白名单(只读)中进行选择(这也不是 100% 安全的)

另一个缺点是您需要访问数据库两次。一次获取包含有关 AutoIncrement 列的信息的架构,然后获取一个填充数据表的架构。

【讨论】:

  • 如果你将表名包装在 QUOTENAME 中,它可以防止 sql 注入,但白名单在这里仍然是一个非常合适的东西。有没有办法也排除这样的计算列?
  • 计算列是指Expression属性不为空或不为空的列吗?
  • 我想是的。我不知道这在 dotnet 方面是如何工作的。还有一些其他潜在问题,例如加密列等,这些问题是安全工作所必需的……但 OP 似乎不太担心这是一个非常灵活的解决方案。
  • 嗯,只是将标识值更改为其他值的想法有点可疑。我们应该真正尝试了解他要求背后的真正原因。
猜你喜欢
  • 1970-01-01
  • 2014-08-24
  • 1970-01-01
  • 1970-01-01
  • 2013-01-01
  • 2017-09-24
  • 1970-01-01
  • 2018-12-16
  • 2018-11-23
相关资源
最近更新 更多