我已经实现了下面描述的 CLR 存储过程,并且它运行得很好。但我不确定我是否真的需要 CLR 来完成此任务,或者中间层解决方案是否同样具有高性能和可维护性。
在现有的代码库中,该公司多年来已经积累了 500 多个搜索存储过程。现在他们希望我编写一个适用于所有这些存储过程的聚合引擎。他们系统中的每个搜索存储过程都遵循类似的格式,因此我知道如何使用正确的参数等以编程方式调用它们。
我不想以任何方式修改每个搜索存储过程。我想做的是首先将存储过程的结果插入到临时表中。然后我可以通过查询临时表来运行我的聚合引擎。
问题是,在 SQL Server 中,除非您知道存储过程结果的确切架构,否则您无法插入存储过程的结果。但这实际上是不可能的,因为存储过程可以根据参数返回不同的结果模式。
因此,为了保证存储过程返回我所期望的确切架构,我创建了一个“SP_Wrapper”CLR 存储过程。在这个包装器中,我调用一个存储过程并将每条记录“调整”到我预期的模式。然后我返回调整后的结果集。
然后我可以插入到我的临时表中,并且知道架构是正确的。
现在,假设我调整了中间层的结果。我必须首先将结果返回到中间层。迭代它们,调整每条记录,然后单独插入或批量复制。
这似乎是正确的选择,但现在我必须部署这个 CLR 存储过程。我在这里真的收获很多吗?
using (var conn = new SqlConnection("context connection=true"))
{
conn.Open();
//load result table schema
resultColumns = SqlSchema.getTempTableMeta(conn, resultTableName);
//load parameter table schema - may not exist
var hasParams = !String.IsNullOrEmpty(paramTableName);
parameters = SqlSchema.getTempTableMeta(conn, paramTableName);
SqlCommand command;
SqlDataReader reader = null;
///Load Parameter Values
if (hasParams)
{
command = conn.CreateCommand();
command.CommandText = $@"if( object_id('tempdb..{paramTableName}') is not null) select top 1 * from {paramTableName};";
command.CommandType = CommandType.Text;
reader = command.ExecuteReader();
using (reader)
{
while (reader.Read())
{
foreach (var p in parameters)
{
var val = reader[p.Name];
if (!String.IsNullOrWhiteSpace(val?.ToString()))
parameter_values[p.Name] = val;
}
}
}
}
SqlDataRecord record = new SqlDataRecord(resultColumns.ToArray());
//////mark the beginning of the result set
SqlContext.Pipe.SendResultsStart(record);
command = conn.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = spName;
foreach (var p in parameters)
{
if (parameter_values.ContainsKey(p.Name))
command.Parameters.Add(
new SqlParameter
{
ParameterName = p.Name,
SqlDbType = p.SqlDbType,
Value = parameter_values[p.Name]
}
);
}
var cmdReader = command.ExecuteReader();
using (cmdReader)
{
while (cmdReader.Read())
{
int sequence = 0;
foreach (var resultColumn in resultColumns)
{
var resultColumnValue = cmdReader[resultColumn.Name];
var t = resultColumn.SqlDbType;
resultColumnValue = SqlSchema.Convert(resultColumnValue, SqlSchema.sqlTypeMap[t]);
record.SetValue(sequence, resultColumnValue);
sequence++;
}
SqlContext.Pipe.SendResultsRow(record);
}
}
// Mark the end of the result-set.
SqlContext.Pipe.SendResultsEnd();
conn.Close();
}
最佳答案
原则上这个解决方案是有意义的。您正在使用 SQL CLR 作为适配器来转换为已知架构。您编写的代码看起来也很高效。
缺点是 SQL CLR 代码比普通代码更难编写、更难测试、更难部署。
这种权衡是否适合您取决于您的性能需求和开发人员生产力需求。这种数据复制真的消耗那么多时间值得接触 SQL CLR 吗?!可能会,也可能不会。
另一种更快的解决方案是为必须调用的每个过程生成 SQL 代码。不要手写。相反,让工具确定该过程的确切架构并输出完美的 T-SQL,将数据以正确的格式直接传送到正确的目标。
这个工具确实可以是一个 SQL CLR 过程,它生成代码然后执行它。或者,它可以是基于 C# 的代码生成器。
关于sql-server - 这是 SqlCLR 的有效用例吗?调整存储过程结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52688691/