1. ホーム
  2. sql

SQL Server がストアドプロシージャ内の varchar を無意識に切り捨ててしまう

2023-11-23 09:50:57

質問

によると このフォーラムの議論 によると、SQL Server (私は 2005 を使用していますが、これは 2000 や 2008 にも適用されると思います) は、すべての varchar を使用して直接文字列を挿入しても、ストアドプロシージャのパラメータとして指定した INSERT を使って直接挿入すると、実際にはエラーが発生します。

CREATE TABLE testTable(
    [testStringField] [nvarchar](5) NOT NULL
)

を実行すると、以下のようになります。

INSERT INTO testTable(testStringField) VALUES(N'string which is too long')

エラーが出ます。

String or binary data would be truncated.
The statement has been terminated.

素晴らしい。 データの整合性が保たれ、呼び出し元もそれを知っています。 では、それを挿入するためのストアドプロシージャを定義してみましょう。

CREATE PROCEDURE spTestTableInsert
    @testStringField [nvarchar](5)
AS
    INSERT INTO testTable(testStringField) VALUES(@testStringField)
GO

と入力し、実行します。

EXEC spTestTableInsert @testStringField = N'string which is too long'

エラーなし、1行に影響あり。 行がテーブルに挿入され testStringField を 'strin' としてテーブルに挿入されました。 SQL Server はストアド プロシージャの varchar パラメータが切り捨てられました。

さて、この動作は時に便利かもしれませんが、これをオフにする方法はないようです。 これは非常に迷惑なことで、私は 欲しい ストアドプロシージャに長すぎる文字列を渡すとエラーになるようにしたいからです。 これに対処する方法は2つあるようです。

まず、ストアドプロシージャの @testStringField パラメータをサイズ6と宣言し、その長さが5以上であるかどうかをチェックします。 これは少しハックしているようで、苛立たしい量の定型的なコードを含んでいます。

第二に、ストアドプロシージャのすべてのvarcharパラメータを、以下のように宣言します。 varchar(max) と宣言し、その上で INSERT ステートメントをストアドプロシージャ内で失敗させます。

後者は問題なく動作しているようです。そこで質問ですが、ストアドプロシージャ内で varchar(max) もし、長すぎる文字列が渡されたときにストアド プロシージャを失敗させたい場合、SQL Server ストアド プロシージャで文字列に ALWAYS を使用するのはよい考えでしょうか。 ベストプラクティスと言えるのでしょうか? 無効化できない無言の切り捨ては、私には愚かなことのように思えます。

どのように解決するのですか?

それはただ です。 .

私のチェックの1つは、パラメータがテーブルのカラムの長さと一致していることを確認することなので、私は問題に気づいたことはありません。クライアント コードでも同様です。個人的には、SQL が長すぎるデータを見ることはないと思っています。もし、切り捨てられたデータを見たとしても、その原因は一目瞭然でしょう。

varchar(max) の必要性を感じるのであれば、以下の理由により、大規模なパフォーマンスの問題に注意してください。 データ型の優先順位 varchar(max) は varchar(n) よりも優先順位が高いです(一番長いものが一番高い)。したがって、このタイプのクエリでは、シークではなくスキャンが行われ、すべてのvarchar(100)の値はvarchar(max)にCASTされることになります。

UPDATE ...WHERE varchar100column = @varcharmaxvalue

編集する

には Microsoft Connectの項目を開く があります。

に含める価値があるでしょう。 Erland Sommarkog の厳格な設定 (そして に一致するコネクト項目 ).

Martinsのコメントを受けて、2回目の編集を行いました。

DECLARE @sql VARCHAR(MAX), @nsql nVARCHAR(MAX);
SELECT @sql = 'B', @nsql = 'B'; 
SELECT 
   LEN(@sql), 
   LEN(@nsql), 
   DATALENGTH(@sql), 
   DATALENGTH(@nsql)
;

DECLARE @t table(c varchar(8000));
INSERT INTO @t values (replicate('A', 7500));

SELECT LEN(c) from @t;
SELECT 
   LEN(@sql + c), 
   LEN(@nsql + c), 
   DATALENGTH(@sql + c), 
   DATALENGTH(@nsql + c) 
FROM @t;