使用 SqlBulkCopy 将 DEFAULT 关键字传递给不可为空的列
Pass DEFAULT keyword to non-nullable column with SqlBulkCopy
有没有办法将 DEFAULT 关键字从 IDataReader 传递给 SqlBulkCopy?
基本上相当于
INSERT INTO MyTable(NonNullableWithDefault)
VALUES (DEFAULT)
在我的应用程序中,目标 table 具有不可为 null 的列,这些列具有默认约束。
有些要上传的数据具有这些列的值,有些则没有。
对于没有值(DBNull)的行,它应该使用默认约束。
如 this answer 中所述,SqlBulkCopy 要么希望您根本不传入 ColumnMapping,要么发送一个值。
我正在考虑的备选方案
- 拆分数据以传入不同的列映射(复杂)
- 检查数据库的默认值并将它们填入数据读取器(慢)
- 创建单独的转储 table 以某种方式不受限制(复杂,可能很慢)
我不是第一个遇到这个问题的人。
人们在这种情况下使用什么?
更新
经过多次摆弄后,我现在用一个查询获取我的整个数据库结构,然后将该数据集传递给我的数据读取器。
SELECT
columns.TABLE_SCHEMA schemaName
, columns.TABLE_NAME tableName
, columns.TABLE_SCHEMA + '.' + columns.TABLE_NAME fullTableName
, columns.COLUMN_NAME columnName
, columns.DATA_TYPE dataType
, ISNULL(columns.CHARACTER_MAXIMUM_LENGTH, -1) charlength
, columns.COLUMN_DEFAULT defaultValue
, columns.ORDINAL_POSITION ordinalPosition
FROM
information_schema.columns columns
默认值字符串的解析不完美,很可能不适合高级情况。然而,在我们的案例中,它涵盖了所有基础。
private object ParseDefault(DataRow row)
{
if(row.IsNull("defaultValue")) return null;
var value = row.Field<string>("defaultValue");
if (value == "(getdate())")
{
return DateTime.UtcNow;
}
var type = GetTypeForName(row.Field<string>("dataType"));
return ParseCell(value.Trim('(', ')'), type);
}
private static object ParseCell(string value, Type info)
{
if (value.Equals("NULL", StringComparison.OrdinalIgnoreCase))
{
return DBNull.Value;
}
int intValue;
if (info == typeof(bool) && int.TryParse(value, out intValue))
{
return intValue == 1;
}
var foo = TypeDescriptor.GetConverter(info);
return foo.ConvertFromInvariantString(value);
}
优点:
- 不可为 null 的默认值解决方法
- 清除验证消息(如最大长度)
- 处理列名大小写匹配
缺点
- 正在解析默认值字符串
- 无法解析 sql 侧默认值(如 sprocs)
Is there a way to pass the DEFAULT keyword to SqlBulkCopy from an IDataReader?
很遗憾,没有。 :-( 如果列是 NOT NULL
并且您的批量插入映射到它,则每一行都必须具有该列的值。
背景:在 TDS 级别,通过发送包含列元数据然后是行数据的 SQL INSERT
statement written using the external tools only syntax followed by TDS structures 来完成批量插入。 None 其中提供了一种表达方式 "if a NULL
value is in a non-NULL
able column, treat the NULL
as a DEFAULT
."
What do people use in such a scenario?
嗯...当感觉应该有内置的、简单的解决方案而不是所有选项都令人痛苦时,这令人沮丧。 :-/ 在不了解您的情况的情况下,很难知道推荐哪个选项。
一个附加选项:占位符值方法。在插入之前,将“NULL
s that should be defaulted”替换为占位符值。然后,post-插入,运行 更新以用数据库生成的默认值(例如 UPDATE ... SET Col1 = DEFAULT WHERE Col1 = *placeholder*
)替换占位符。您的应用程序必须知道哪些不可为 null 的列具有默认值,但不必知道如何计算默认值。我不关心它,因为它使用所谓的幻数——但它是一个选项。
有没有办法将 DEFAULT 关键字从 IDataReader 传递给 SqlBulkCopy?
基本上相当于
INSERT INTO MyTable(NonNullableWithDefault)
VALUES (DEFAULT)
在我的应用程序中,目标 table 具有不可为 null 的列,这些列具有默认约束。
有些要上传的数据具有这些列的值,有些则没有。
对于没有值(DBNull)的行,它应该使用默认约束。
如 this answer 中所述,SqlBulkCopy 要么希望您根本不传入 ColumnMapping,要么发送一个值。
我正在考虑的备选方案
- 拆分数据以传入不同的列映射(复杂)
- 检查数据库的默认值并将它们填入数据读取器(慢)
- 创建单独的转储 table 以某种方式不受限制(复杂,可能很慢)
我不是第一个遇到这个问题的人。
人们在这种情况下使用什么?
更新
经过多次摆弄后,我现在用一个查询获取我的整个数据库结构,然后将该数据集传递给我的数据读取器。
SELECT
columns.TABLE_SCHEMA schemaName
, columns.TABLE_NAME tableName
, columns.TABLE_SCHEMA + '.' + columns.TABLE_NAME fullTableName
, columns.COLUMN_NAME columnName
, columns.DATA_TYPE dataType
, ISNULL(columns.CHARACTER_MAXIMUM_LENGTH, -1) charlength
, columns.COLUMN_DEFAULT defaultValue
, columns.ORDINAL_POSITION ordinalPosition
FROM
information_schema.columns columns
默认值字符串的解析不完美,很可能不适合高级情况。然而,在我们的案例中,它涵盖了所有基础。
private object ParseDefault(DataRow row)
{
if(row.IsNull("defaultValue")) return null;
var value = row.Field<string>("defaultValue");
if (value == "(getdate())")
{
return DateTime.UtcNow;
}
var type = GetTypeForName(row.Field<string>("dataType"));
return ParseCell(value.Trim('(', ')'), type);
}
private static object ParseCell(string value, Type info)
{
if (value.Equals("NULL", StringComparison.OrdinalIgnoreCase))
{
return DBNull.Value;
}
int intValue;
if (info == typeof(bool) && int.TryParse(value, out intValue))
{
return intValue == 1;
}
var foo = TypeDescriptor.GetConverter(info);
return foo.ConvertFromInvariantString(value);
}
优点:
- 不可为 null 的默认值解决方法
- 清除验证消息(如最大长度)
- 处理列名大小写匹配
缺点
- 正在解析默认值字符串
- 无法解析 sql 侧默认值(如 sprocs)
Is there a way to pass the DEFAULT keyword to SqlBulkCopy from an IDataReader?
很遗憾,没有。 :-( 如果列是 NOT NULL
并且您的批量插入映射到它,则每一行都必须具有该列的值。
背景:在 TDS 级别,通过发送包含列元数据然后是行数据的 SQL INSERT
statement written using the external tools only syntax followed by TDS structures 来完成批量插入。 None 其中提供了一种表达方式 "if a NULL
value is in a non-NULL
able column, treat the NULL
as a DEFAULT
."
What do people use in such a scenario?
嗯...当感觉应该有内置的、简单的解决方案而不是所有选项都令人痛苦时,这令人沮丧。 :-/ 在不了解您的情况的情况下,很难知道推荐哪个选项。
一个附加选项:占位符值方法。在插入之前,将“NULL
s that should be defaulted”替换为占位符值。然后,post-插入,运行 更新以用数据库生成的默认值(例如 UPDATE ... SET Col1 = DEFAULT WHERE Col1 = *placeholder*
)替换占位符。您的应用程序必须知道哪些不可为 null 的列具有默认值,但不必知道如何计算默认值。我不关心它,因为它使用所谓的幻数——但它是一个选项。