web-dev-qa-db-ja.com

エラープロシージャを修正するには、タイプ 'ntext / nchar / nvarchar'のパラメータ '@parameters'が必要ですか?

ストアドプロシージャを使用して、テーブルの結果、および関連するテーブルまたはデータベースへの最近の変更を表示しようとしています。ストアドプロシージャは次のとおりです。

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ステートメントの結果に加えてアイテムに戻ることになっています。ストアドプロシージャへの入力はありません。ストアドプロシージャはSQLManagement Studio(2008)で正常にコンパイルされますが、ページにエラーが返されます:Microsoft OLE DB Provider for SQLServerエラー '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コードにあることを意味します。私の人生では、なぜこのエラーが発生しているのか特定できません。

9
Techmaniac

この回答では、質問で言及した問題を再現し、これをどのように解決したかについても説明します。

まず、Create Tables Scriptセクションのスクリプトを使用して、dbo.MKTG_Recentdbo.Table_1という名前の2つのテーブルを作成しましょう。これらのテーブルは、質問で提供されたデータを使用して作成したいくつかの仮定に基づいて作成しました。スクリプトを使用すると、テーブル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 を読んだ後、ストアドプロシージャの2番目のパラメータが次のタイプを定義していることがわかりました。出力パラメータであり、Unicode文字列である必要があります。そこで、2番目のパラメーターをUnicode文字列としてNをプレフィックスとして指定することにより、ストアドプロシージャを変更しました。ストアドプロシージャの変更については、スクリーンショット#2を参照してください。

スクリーンショット#3は、変更を加えた後のストアドプロシージャdbo.MKTG_Currentの出力を示しています。ストアドプロシージャは2つの出力を生成します。 1つはsp_executesqlに渡される変数@Borkのクエリステートメント用で、もう1つは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

15
user756519

@BorkをNVARCHARとして宣言しました。それで、なぜあなたは言うのですか:

SET @Bork = 'SELECT ...';

?する必要があります:

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

これがNVARCHAR(Unicode)文字列の定義方法です。 Nは全国を表します。そのNプレフィックスを省略すると、sp_executesqlはそれがVARCHARであると見なし、エラーが発生します。

[〜#〜] edit [〜#〜]キーレンの場合

技術的にはそうですが、[〜#〜] can [〜#〜]NプレフィックスなしでNVARCHARリテラルを宣言できますが、いくつかあります絶対にそうすべきではない理由。 1つは、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を使用するのが面倒なのか疑問に思う必要があります)。

3
Aaron Bertrand