如何修复错误过程需要'ntext / nchar / nvarchar'类型的参数'@parameters'?

时间:2011-08-01 21:04:43

标签: sql sql-server stored-procedures asp-classic

我正在尝试使用存储过程来显示表的结果,以及相关表或对数据库的最新更改。存储过程是:

set ANSI_NULLS ON
set NOCOUNT ON
set QUOTED_IDENTIFIER ON
go


ALTER PROCEDURE [dbo].[MKTG_Current]( @added smalldatetime OUTPUT, @named varchar(50) OUTPUT)
AS

DECLARE @pDate smalldatetime;
DECLARE @AIID int;
DECLARE @Table varchar(50);
DECLARE @Bork nvarchar(350);
SET @pDate = GETDATE()


SELECT @Table=[Table], @AIID=AIID, @added=date_added  FROM MKTG_Recent WHERE date_added > DATEDIFF(day, date_added, DATEADD(DD, 30, @pDate))
SET @named = @Table
SET @Bork = 'SELECT * FROM ' + QUOTENAME(@Table) + ' WHERE AIID= ' + cast(@AIID as varchar(100))

EXECUTE sp_executesql @Bork, @added OUTPUT, @named OUTPUT


SELECT @added, @named 

除了select语句的结果之外,它应该返回项目。存储过程没有输入。存储过程在SQL Management Studio(2008)中编译正常,但页面返回错误: 用于SQL Server的Microsoft OLE DB提供程序错误“80040e14”

过程需要'ntext / nchar / nvarchar'类型的参数'@parameters'。 index.asp,第61行

页面第61行以粗体显示:

  dim Objrs, cmd
  set Objrs = Server.CreateObject("ADODB.RecordSet")
  set cmd = Server.CreateObject("ADODB.Command")
  set conn = Server.CreateObject("ADODB.Connection")
  conn.Open strConnect
  set cmd.ActiveConnection = conn
  cmd.CommandText="MKTG_Current"
  cmd.CommandType=adCmdStoredProc
  cmd.Parameters.Append cmd.CreateParameter("@added", 135, 2)
  cmd.Parameters.Append cmd.CreateParameter("@named", 200, 2, 50)
Line 61 **set Objrs = cmd.Execute**
  name_of_table = cmd.Parameters("@named")
  added = cmd.Parameters("@added")  

我的印象是这是由SQL代码错误引起的,但我没有看到它。快速检查Objrs.state返回0,这意味着问题肯定在于SQL代码。对于我的生活,我无法确定为什么会产生这个错误。

2 个答案:

答案 0 :(得分:15)

在这个答案中,我将尝试重新创建您在问题中提到的问题,并解释我是如何解决这个问题的。

首先,让我们使用创建表脚本部分下的脚本创建两个名为dbo.MKTG_Recentdbo.Table_1的表。我根据我使用问题中提供的数据做出的一些假设创建了这些表。使用该脚本,表dbo.MKTG_Recent将填充1条记录。

接下来,使用创建存储过程脚本部分下提供的脚本创建名为dbo.MKTG_Current的存储过程。

如果我们尝试使用EXEC命令EXEC MKTG_Current null, null执行存储过程,则会抛出错误消息 Msg 214, Level 16, State 3, Procedure sp_executesql, Line 1 Procedure expects parameter '@parameters' of type 'ntext/nchar/nvarchar'. 。参考屏幕截图#1

通过MSDN阅读有关过程sp_executesql的用法的内容后,我发现存储过程的第二个参数定义了输出参数的类型,它必须是Unicode字符串。因此,我通过在N前面添加第二个参数作为Unicode字符串来修改存储过程。请参阅屏幕截图#2 以获取存储过程更改。

屏幕截图#3 显示更改后存储过程dbo.MKTG_Current的输出。存储过程将产生两个输出。一个用于变量@Bork中的查询语句,它被传递给sp_executesql,另一个输出对应于显示OUTPUT变量的SELECT语句。

根据要求,我不确定您是否需要调用sp_executesql,您可以编写存储过程,如简化存储过程部分所示。我错了,因为我不完全理解这个要求。屏幕截图#4 显示简化存储过程的输出。 SELECT 语句不是必需的,因为值是通过 OUTPUT 参数传递的。我只包含了 SELECT 语句来显示查询输出。

希望这能指出你正确的方向。

创建表脚本:

CREATE TABLE [dbo].[MKTG_Recent](
    [Table] [varchar](40) NOT NULL,
    [AIID] [int] NOT NULL,
    [date_added] [datetime] NOT NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Table_1](
    [AIID] [int] NOT NULL,
    [added] [smalldatetime] NOT NULL,
    [named] [varchar](50) NOT NULL
) ON [PRIMARY]
GO

INSERT INTO dbo.MKTG_Recent ([Table], AIID, date_added) 
VALUES ('Table_1', 1, '2011-08-01')
GO

创建存储过程脚本:

SET ANSI_NULLS ON
SET NOCOUNT ON
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[MKTG_Current]
(       @added  SMALLDATETIME   OUTPUT
    ,   @named  VARCHAR(50)     OUTPUT
)
AS

DECLARE @pDate  SMALLDATETIME;
DECLARE @AIID   INT;
DECLARE @Table  VARCHAR(50);
DECLARE @Bork   NVARCHAR(350);

SET     @pDate  = GETDATE()

SELECT  @Table  = [Table]
    ,   @AIID   = AIID
    ,   @added  = date_added  
FROM    dbo.MKTG_Recent 
WHERE   date_added > DATEDIFF(day, date_added, DATEADD(DD, 30, @pDate))

SET @named  = @Table
SET @Bork   = ' SELECT  * 
                FROM    ' + QUOTENAME(@Table) + ' 
                WHERE   AIID= ' + CAST(@AIID AS VARCHAR(100))

EXECUTE sp_executesql   @Bork
                    ,   @added OUTPUT
                    ,   @named OUTPUT

SELECT  @added
    ,   @named 
GO    

简化存储过程:

SET ANSI_NULLS ON
SET NOCOUNT ON
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[MKTG_Current]
(       @added  SMALLDATETIME   OUTPUT
    ,   @named  VARCHAR(50)     OUTPUT
)
AS

DECLARE @Table  VARCHAR(50);

SELECT  @named  = [Table]
    ,   @added  = date_added  
FROM    dbo.MKTG_Recent 
WHERE   date_added > DATEDIFF(day, date_added, DATEADD(DD, 30, GETDATE()))

SELECT  @added AS added
    ,   @named AS named 
GO

<强>截图:

#1:执行显示错误消息

Error_Message

#2:对存储过程进行的更改

Modified_SP

#3:更改后的存储过程输出

Stored_procedure_output

#4:简化的存储过程输出

Simplified_stored_procedure_output

答案 1 :(得分:4)

您将@Bork声明为NVARCHAR。那你为什么这么说:

SET @Bork = 'SELECT ...';

?应该是:

SET @Bork = N'SELECT ...';

这是我们定义NVARCHAR(Unicode)字符串的方式。 N代表国家。如果您省略该N前缀,sp_executesql会认为它是VARCHAR,并且会导致错误。

Kieren的

编辑

虽然技术上是肯定的,但你 CAN 在没有N前缀的情况下声明NVARCHAR文字,有几个原因你不应该这样做。一种是避免user873479正在接收的错误。其他人要确保正确的结果。一些例子:

(A)让我们尝试使用N前缀而不使用前缀sp_executesql。即使字符串中没有实际的Unicode字符,但在调用sp_executesql时忘记输入N前缀会导致出现完全相同的错误:

EXEC sp_executesql N'SELECT 1';
EXEC sp_executesql 'SELECT 1';

结果

====
1
====
Msg 214, Level 16, State 2, Procedure sp_executesql, Line 1
Procedure expects parameter '@statement' of type 'ntext/nchar/nvarchar'.

(B)现在让我们尝试将Unicode字符简单地分配给NVARCHAR变量。请注意,如果没有N前缀,实际值会丢失吗?

DECLARE @x NVARCHAR(32) = 'Ǝ';
SELECT @x;
SET @x = N'Ǝ';
SELECT @x;

结果

====
?
====
Ǝ

(C)现在让我们更进一步。我们将一些Unicode数据放入表中:

DECLARE @foo TABLE(bar NVARCHAR(1));
INSERT @foo(bar) SELECT N'Ǝ';

-- now someone comes along looking for the row, without using N:
SELECT COUNT(*) FROM @foo WHERE bar = 'Ǝ';

结果

====
0

我可以想出更多示例,其中隐含地在CHAR / VARCHAR和NCHAR / NVARCHAR之间切换可以将搜索转换为扫描,但我认为错误消息和不正确的结果现在应该足够了。

当然,您可以在不使用N前缀的情况下声明NVARCHAR文字,但前提是您没有调用期望NVARCHAR的过程,并且仅当您的数据实际上没有包括任何Unicode字符(在这种情况下,我不得不想知道为什么你首先要使用NVARCHAR。)