【问题标题】:When executing a stored procedure, what is the benefit of using CommandType.StoredProcedure versus using CommandType.Text?执行存储过程时,使用 CommandType.StoredProcedure 与使用 CommandType.Text 相比有什么好处?
【发布时间】:2012-02-13 19:54:22
【问题描述】:

所以在 C# 中使用存储过程我有如下代码(省略连接代码):

 string sql = "GetClientDefaults";

 SqlCommand cmd = new SqlCommand(sql);
 cmd.CommandType = CommandType.StoredProcedure;    //<-- DO I NEED THIS??
 cmd.Parameters.AddWithValue("@computerName", computerName);

其中 sql 是存储过程的名称。现在,无论有没有注释行,这段代码似乎都可以正常工作。

那么,我需要这条线吗?设置它是否有一些性能(或其他)好处?不设置或设置为 Text 有什么好处吗?

【问题讨论】:

    标签: c# sql sql-server database


    【解决方案1】:

    根据this blog post 中的测试,当您使用CommandType.Text 时,SQL Server 将通过将您的语句包装在 sp_executesql 中为您进行参数化。但是当您使用CommandType.StoredProcedure 时,您将对其进行参数化,从而为数据库节省一些工作。后一种方法更快。

    编辑:

    设置

    我自己做了一些测试,结果如下。

    创建此过程:

    create procedure dbo.Test
    (
       @Text1 varchar(10) = 'Default1'
      ,@Text2 varchar(10) = 'Default2'
    )
    as
    begin
       select @Text1 as Text1, @Text2 as Text2
    end
    

    使用 SQL Server Profiler 为其添加跟踪。

    然后使用以下代码调用它:

    using System;
    using System.Data;
    using System.Data.SqlClient;
    
    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main()
            {
                CallProcedure( CommandType.Text );
                CallProcedure( CommandType.StoredProcedure );
            }
    
            private static void CallProcedure(CommandType commandType)
            {
                using ( SqlConnection connection = new SqlConnection("Data Source=localhost;Initial Catalog=Test;Integrated Security=SSPI;") )
                {
                    connection.Open();
                    using ( SqlCommand textCommand = new SqlCommand("dbo.Test", connection) )
                    {
                        textCommand.CommandType = commandType;
                        textCommand.Parameters.AddWithValue("@Text1", "Text1");
                        textCommand.Parameters.AddWithValue("@Text2", "Text2");
                        using ( IDataReader reader = textCommand.ExecuteReader() )
                        {
                            while ( reader.Read() )
                            {
                                Console.WriteLine(reader["Text1"] + " " + reader["Text2"]);
                            }
                        }
                    }
                }
            }
        }
    }
    

    结果

    在这两种情况下,调用都是使用 RPC 进行的。

    以下是使用CommandType.Text 跟踪显示的内容:

    exec sp_executesql N'dbo.Test',N'@Text1 nvarchar(5),@Text2 nvarchar(5)',@Text1=N'Text1',@Text2=N'Text2'
    

    这是使用CommandType.StoredProcedure的结果:

    exec dbo.Test @Text1=N'Text1',@Text2=N'Text2'
    

    如您所见,文本调用包含在对sp_executesql 的调用中,因此可以正确参数化。这当然会产生轻微的开销,因此我之前关于使用CommandType.StoredProcedure 更快的说法仍然成立。

    另一件值得注意的事情,这也是一个交易破坏者,当我创建没有默认值的过程时,我得到了以下错误:

    消息 201,级别 16,状态 4,程序测试,第 0 行程序或 函数“测试”需要参数“@Text1”,但未提供。

    原因是对sp_executesql 的调用是如何创建的,如您所见,参数已声明和初始化,但未使用它们。对于工作的调用,它应该看起来像这样:

    exec sp_executesql N'dbo.Test @Text1, @Text2',N'@Text1 nvarchar(5),@Text2 nvarchar(5)',@Text1=N'Text1',@Text2=N'Text2'
    

    意思是,当您使用CommandType.Text 时,您必须将参数添加到CommandText,除非您总是希望使用默认值。

    所以,回答你的问题

    1. 使用CommandType.StoredProcedure 更快。
    2. 如果您使用CommandType.Text,则必须将参数名称添加到对过程的调用中,除非您希望使用默认值。

    【讨论】:

    • -那么使用StoredProcedure可能会更快?
    • @MAW74656 是的。另请注意 Panagiotis Kanavos 回答可能存在除 SQL Server 之外的提供程序,除非您指定它,否则它不会理解您正在尝试执行 proc。
    • -我理解其他供应商的观点,只是这个应用程序不太可能需要那种改变。并且有许多商业企业应用程序需要您使用 SQL Server(任何特定的数据库服务器),我敢打赌他们不会在那里使用提供程序工厂。
    • -这里有很多很棒的工作!我剩下的唯一问题是两者在无参数存储过程中的区别。当有参数时,我使用 StoredProcedure 并将参数添加到 Command 对象的参数集合中。
    • @MAW74656 我可以看到调用无参数过程的唯一区别是使用CommandType.StoredProcedure 稍微快一些。
    【解决方案2】:

    实际上有很大的不同。如果您指定命令类型StoredProcedure,那么您添加到SqlCommand 的任何参数都将是添加到过程调用 的参数。如果您将其保留为Text,则参数将被添加到batch,而不是过程中。为了说明这一点,让我们创建一个虚拟程序:

    create procedure usp_test 
        @p1 char(10)  = 'foo',
        @p2 int = 42
    as
        select @p1, @p2;    
    go
    

    然后编译这个小小的 C# 应用程序:

       static void Main(string[] args)
        {
            ExecWithType(CommandType.Text);
            ExecWithType(CommandType.StoredProcedure);
        }
    
        static void ExecWithType(CommandType type)
        {
            using (SqlConnection conn = new SqlConnection(Settings.Default.connString))
            {
                conn.Open();
                using (SqlCommand cmd1 = new SqlCommand("usp_test", conn))
                {
                    cmd1.CommandType = type;
                    cmd1.Parameters.AddWithValue("@p1", "bar");
                    cmd1.Parameters.AddWithValue("@p2", 24);
                    using (SqlDataReader rdr = cmd1.ExecuteReader())
                    {
                        while (rdr.Read())
                        {
                            Console.WriteLine("Type: {0} Result: @p1: {1} @p2: {2}", type, rdr[0], rdr[1]);
                        }
                    }
                }
            }
        }
    

    结果是:

    Type: Text Result: @p1: foo        @p2: 42
    Type: StoredProcedure Result: @p1: bar        @p2: 24
    

    哎哟!对于CommandType.Text 设置,虽然参数被传递到batch,但它们没有被传递到procedure。许多小时调试乐趣的来源...

    【讨论】:

    • -所以有了参数,没有灰色区域,CommandType.StoredProcedure 肯定更好、更准确、更快。
    【解决方案3】:

    您可以设置它以允许 ADO.NET 帮助您。当您使用CommandType.StoredProcedure 时,您只需将CommandText 设置为等于存储过程名称。

    例如,这个:

    YourSqlCommand.CommandType = CommandType.StoredProcedure;
    YourSqlCommand.CommandText = "dbo.YourStoredProc";
    

    相当于:

    YourSqlCommand.CommandText = "exec dbo.YourStoredProc";
    

    【讨论】:

    • -我不是说“Exec”或“dbo”。在任一版本中,它都可以正常工作。还有其他方法可以帮助吗?
    • 我实际上会重新编译它.. 调试它并真正测试它.. 因为我没有看到类似工作而无需设置 CommandTpe...嗯嗯???
    • @MAW74656 这是因为在 SQL Server 中,如果存储的 proc 是批处理的第一条语句,则不必输入 exec
    • @Shark -OK,这解释了那部分,但这是设置命令类型的唯一原因吗?制作更长的语法?一定有benefit
    • @MAW74656 对于无参数查询或存储过程,使用不同的 CommandType 可能没有实现差异。我会运行 SQL Profiler 并查看它们在数据库中的表现,但我敢打赌它们看起来会相同。
    【解决方案4】:

    CommandType 并非特定于 SQL Server。它是IDbCommand 接口的一个属性,指示底层提供程序以特定方式处理CommandText。虽然 SQL Server 可能将单字名称视为过程,但您不应期望这在其他提供程序中也能正常工作。

    一般来说,您应该更喜欢使用提供程序生成的类(如 DbCommand)而不是特定类(如 SqlCommand)。这样,您只需更改配置文件中的提供程序字符串即可针对不同的数据库。

    【讨论】:

    • @PagagiotisKanavos - 我认为你在这里打的是一场不同的战斗。网上的大部分示例代码都使用了 SQLCommand。但是即使我接受这个前提,你仍然没有说设置 CommandType 有什么效果。
    • 没有战斗可打。您使用特定的类,您与提供者绑定,并且必须重写所有内容。示例代码不是生产代码。至于 CommandType 的作用——您认为您可以通过传递名称并假设默认的 CommandType.Text 在 Oracle 中执行存储过程吗?无论如何,我永远不会相信生产代码中的意外和未记录的行为,只是为了避免设置值。
    • -但是设置它有什么用呢?
    • 感谢您的洞察力和建议。很有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-12
    相关资源
    最近更新 更多