SQL Server молча обрезает varchar в хранимых процедурах - PullRequest
65 голосов
/ 07 января 2011

Согласно этому обсуждению на форуме , SQL Server (я использую 2005, но я понимаю, что это относится и к 2000 и 2008) молча обрезает любые varchar, которые вы указываете как параметры хранимой процедуры, до длины из 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, если я действительно хочу, чтобы хранимый процесс не выполнялся при передаче слишком длинной строки ? Может ли это быть даже лучшей практикой? Молчание, которое нельзя отключить, кажется мне глупым.

Ответы [ 6 ]

29 голосов
/ 07 января 2011

Это просто , это .

Я никогда не замечал проблемы, потому что одной из моих проверок было бы убедиться, что мои параметры соответствуют длине столбцов моей таблицы. В коде клиента тоже. Лично я ожидал бы, что SQL никогда не увидит слишком длинные данные. Если бы я увидел усеченные данные, было бы ясно, что вызвало их.

Если вы чувствуете необходимость varchar (max), остерегайтесь серьезной проблемы с производительностью из-за приоритета типа данных . varchar (max) имеет более высокий приоритет, чем varchar (n) (самый длинный - самый высокий). Таким образом, в этом типе запроса вы будете сканировать, а не искать, и каждое значение varchar (100) от CAST до varchar (max)

UPDATE ...WHERE varchar100column = @varcharmaxvalue

Edit:

Существует открытый элемент Microsoft Connect относительно этой проблемы.

И, вероятно, его стоит включить в Строгие настройки Эрланда Соммаркога соответствующий элемент подключения ).

Редактировать 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;
15 голосов
/ 19 марта 2011

Спасибо, как всегда, StackOverflow за проведение такого рода углубленного обсуждения. Недавно я просматривал свои хранимые процедуры, чтобы сделать их более устойчивыми, используя стандартный подход к транзакциям и блоки try / catch. Я не согласен с Джо Стефанелли в том, что «я предлагаю сделать приложение ответственным», и полностью согласен с Джезом: «Наличие SQL Server для проверки длины строки было бы намного предпочтительнее». Весь смысл использования хранимых процедур в том, что они написаны на языке, родном для базы данных, и должны выступать в качестве последней линии защиты. На стороне приложения разница между 255 и 256 является просто неизменным числом, но в среде базы данных поле с максимальным размером 255 просто не примет 256 символов. Механизмы проверки приложения должны как можно лучше отражать внутреннюю базу данных, но обслуживание сложно, поэтому я хочу, чтобы база данных дала мне хорошую обратную связь, если приложение по ошибке допускает неподходящие данные. Вот почему я использую базу данных вместо набора текстовых файлов с CSV, JSON или чем-то еще.

Я был озадачен, почему один из моих SP выдал ошибку 8152, а другой молча усек. Я наконец-то запутался: у SP, который выдал ошибку 8152, был параметр, который позволял на один символ больше, чем соответствующий столбец таблицы. Столбец таблицы был установлен в nvarchar (255), но параметр был nvarchar (256). Так что, не моя «ошибка» не решает проблему gbn: «проблема с высокой производительностью»? Вместо использования max, возможно, мы могли бы последовательно установить размер столбца таблицы, скажем, 255, а параметр SP - на один символ длиннее, скажем, 256. Это решает проблему тихого усечения и не снижает производительности. Предположительно, есть еще один недостаток, о котором я не задумывался, но мне он кажется хорошим компромиссом.

Обновление: Боюсь, эта техника не соответствует. Дальнейшее тестирование показывает, что я иногда могу вызвать ошибку 8152, а иногда данные молча усекаются. Буду очень признателен, если кто-нибудь поможет мне найти более надежный способ справиться с этим.

Обновление 2: Пожалуйста, смотрите ответ Pyitoechito на этой странице.

4 голосов
/ 13 июня 2013

Обновление: боюсь, эта техника не соответствует.Дальнейшее тестирование показывает, что я иногда могу вызвать ошибку 8152, а иногда данные молча усекаются.Я был бы очень признателен, если бы кто-нибудь мог помочь мне найти более надежный способ справиться с этим.

Это, вероятно, происходит потому, что 256-й символ в строке является пробелом.VARCHAR s будет обрезать концевые пробелы при вставке и просто выдаст предупреждение.Таким образом, ваша хранимая процедура молча усекает ваши строки до 256 символов, а ваша вставка усекает конечный пробел (с предупреждением).Он выдаст ошибку, если указанный символ не является пробелом.

Возможно, решение будет состоять в том, чтобы сделать хранимую процедуру VARCHAR подходящей длиной для захвата символа, не являющегося пробелом.VARCHAR(512) вероятно будет достаточно безопасно.

4 голосов
/ 07 января 2011

Такое же поведение можно увидеть здесь:

declare @testStringField [nvarchar](5)
set @testStringField = N'string which is too long'
select @testStringField

Я бы предложил возложить на сторону приложения ответственность за проверку ввода перед вызовом хранимой процедуры.

1 голос
/ 19 апреля 2012

Одним из решений было бы:

  1. Изменить все входящие параметры на varchar(max)
  2. Иметь приватную переменную sp с правильной длиной данных (просто скопируйте и вставьте все в параметры ив конце добавить «int»
  3. Объявить переменную таблицы с именами столбцов, совпадающими с именами переменных
  4. Вставить в таблицу строку, в которой каждая переменная входит в столбец с тем же именем
  5. Выберите из таблицы внутренние переменные

Таким образом, ваши изменения в существующем коде будут очень минимальными, как в примере ниже.

Этооригинальный код:

create procedure spTest
(
    @p1 varchar(2),
    @p2 varchar(3)
)

Это новый код:

create procedure spTest
(
    @p1 varchar(max),
    @p2 varchar(max)
)
declare @p1Int varchar(2), @p2Int varchar(3)
declare @test table (p1 varchar(2), p2 varchar(3)
insert into @test (p1,p2) varlues (@p1, @p2)
select @p1Int=p1, @p2Int=p2 from @test

Обратите внимание, что если длина входящих параметров будет больше, чем предел, а не молчаОтрезание строки SQL Server выдаст ошибку.

0 голосов
/ 08 января 2011

Вы всегда можете добавить оператор if в ваш sp, который проверяет их длину, и, если они больше указанной длины, выдает ошибку.Это довольно трудоемкий процесс, и обновление будет проблематичным, если вы обновите размер данных.

...