1

有几个 SQL 服务器具有存储过程,例如 Microsoft SQL Server 或 PostgreSQL。还有几个客户端对象实现存储过程调用(TADOStoredProc在 Delphi 中,SqlCommand在 .NET Framework 中等)。

我一直想问的问题是:

存储过程总是以特殊有效的方式执行,其参数的二进制表示,或者是表示存储过程参数的超级高级对象,总是转换为纯文本字符串,存储过程总是通过将此纯文本字符串发送到 SQL 服务器来执行? (让我们以一种技术为例 - 让它成为 SQL Server 和 ADO.NET)。

我注意到对于 ADO.NET 过程的参数名称没有任何意义——只有它们的创建顺序很重要,这让我想到了纯文本字符串的想法。

@Alex K 的更新。

我在 .NET 中测试了以下代码:

CREATE PROCEDURE paramtest
@par1 nvarchar(50),
@par2 nvarchar(50),
@par3 nvarchar(50)
AS
  SELECT Res = '@par1 = ' + @par1 + '; @par2 = ' + @par2 + '; @par3 = ' + @par3
  RETURN 555

using System;
using System.Data.SqlClient;
using System.Data;

namespace SqlParamTest
{
    class Program
    {
        private static void addParam(SqlCommand cmd, string parameterName, ParameterDirection direction, SqlDbType dbType, int size, object value)
        {
            SqlParameter par = new SqlParameter(parameterName, dbType, size);
            par.Direction = direction;
            par.Value = value;
            cmd.Parameters.Add(par);
        }

        static void Main(string[] args)
        {
            using (SqlConnection conn = new SqlConnection(@"Data Source=localhost\sqlexpress;Initial Catalog=test;Integrated Security=True"))
            {
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.CommandText = "paramtest";
                addParam(cmd, "@par3", ParameterDirection.Input, SqlDbType.NVarChar, 50, "third");
                addParam(cmd, "@par2", ParameterDirection.Input, SqlDbType.NVarChar, 50, "second");
                addParam(cmd, "@par1", ParameterDirection.Input, SqlDbType.NVarChar, 50, "first");
                addParam(cmd, "@Return", ParameterDirection.ReturnValue, SqlDbType.Int, 0, null);
                conn.Open();
                SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                if (rdr.Read()) Console.WriteLine((string)rdr["Res"]);
                rdr.Close();
                Console.WriteLine("Return value: {0}", cmd.Parameters["@Return"].Value);
            }
            Console.ReadKey();
        }
    }
}

是的,它以正确的方式维护参数,但我认为,是 .NET 为参数添加了额外的检查,因为 Delphi 中的代码如下:

procedure TMyClass.Test(Conn: TADOConnection);
var SP:TADOStoredProc;
begin
  SP := TADOStoredProc.Create(nil);
  try
    SP.Connection := Conn;
    SP.ProcedureName := 'paramtest';
    SP.Parameters.CreateParameter('@whatthehell', ftString, pdInput, 50, 'one');
    SP.Parameters.CreateParameter('@AnotherCrap', ftString, pdInput, 50, 'two');
    SP.Parameters.CreateParameter('?', ftString, pdInput, 50, 'three');
    SP.ExecProc;
  finally
    SP.Free;
  end;
end;

返回:

@par1 = one; @par2 = two; @par3 = three

并且不会抱怨缺少参数。

pdReturnValue 仅在此参数在任何其他参数之前创建时才有效。

4

1 回答 1

1

不确定您要寻找什么答案,存储过程命令文本和参数被传递给驱动程序/提供者或通过 ADO.NET 本地传递,将其格式化为 TDS(表格数据流)RPC(远程过程调用)消息,然后传递给正在使用网络协议的服务器;管道,tcp/ip 等。数据以二进制流的形式发送。

如果您有兴趣,可以从 Microsoft 获得TDS 规范。

SQLCommand存储过程调用确实需要一个参数名称,它的 OleDB/ODBC 只关心顺序并?用作参数占位符而不是@NAME.

关于订单

在您的示例中,订单不相关,因为您为服务器提供了正确的参数名称,因此这是发送到服务器的内容:

exec paramtest @par3=N'third',@par2=N'second',@par1=N'first'

这足以让服务器找出正确的参数/顺序。

如果你改为

addParam(cmd, "@xxpar3",
addParam(cmd, "@xxpar2", 
addParam(cmd, "@xxpar1",

服务器会检测到它没有指定的参数xxxpar*,并因“缺少@par1”错误而失败。

如果您进行了修改addParam,因此它没有设置参数名称 .net 将创建默认值:

exec paramtest @Parameter1=N'third',@Parameter2=N'second',@Parameter3=N'first'

这将导致上述错误。

如果您进行了修改addParam,则它没有设置参数名称,然后覆盖自动名称;

cmd.Parameters.Add(par);
par.ParameterName = "";

这是被执行的:

 exec paramtest N'third',N'second',N'first'

导致

 @par1 = third; @par2 = second; @par3 = first

我不知道 Delphi 做了什么…… SQL Server 的完整版附带了一个名为 SQL Profiler 的工具,该工具显示发送到服务器实例的文本数据,因此您可以准确地看到发生了什么。 什么分析器与 sql express 一起使用?

于 2013-04-26T17:16:37.210 回答