sql-server - 这是 SqlCLR 的有效用例吗?调整存储过程结果

标签 sql-server t-sql stored-procedures sqlclr

我已经实现了下面描述的 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/

相关文章:

c# - 使用存储过程进行多重映射的 Dapper

mysql - SQL Server减去MySQL功能

c# - 从数据库问题下载 ASP.NET MVC 3 文件

t-sql - 从存储过程中获取 Top N

sql - 是否可以向表中插入任意数据?

java - 如何使用以下详细信息将 java 对象传递给 oracle 存储过程

sql-server - 是否可以将 Like 与 ALL 关键字一起使用

sql-server - 索引插入期间的多个表假脱机(Eager 假脱机)

c# - 如何在公式中使用 SQL 值

linq - 如何在 LINQ 或 SQL 中按日期获取每个 "type"的最后一个元素