Похоже, что объяснение связано с сочетанием: а) подробностей из связанного блога, которые не были упомянуты в этом вопросе, б) прагматики ТВП, подходящей к тому, как параметры всегда передавались внутрь и наружу, в) и характера табличных переменных.
Недостающая деталь, содержащаяся в связанном посте блога, в точности описывает, как переменные передаются в и из хранимых процедур и функций (что относится к формулировке в вопросе «более безопасной версии передачи по ссылке, если они являются параметрами OUTPUT») :
TSQL использует семантику копирования / копирования для передачи параметров хранимым процедурам и функциям ....
... когда хранимый процесс завершает выполнение (не нажимая на ошибку), создается копия, которая обновляет переданный параметр с учетом любых изменений, внесенных в него в хранимом процессе.
Реальное преимущество этого подхода в случае ошибки. Если во время выполнения хранимой процедуры возникает ошибка, любые изменения, внесенные в параметры, не будут передаваться вызывающей стороне.
Если ключевое слово OUTPUT отсутствует, копирование не производится.
Нижняя строка:
параметры хранимых процедур никогда не отражают частичное выполнение сохраненного процесса, если он обнаружил ошибку.
Первая часть этой головоломки состоит в том, что параметры всегда передаются «по значению». И только когда параметр помечен как OUTPUT
и хранимая процедура успешно завершена, текущее значение действительно отправляется обратно. Если OUTPUT
значения были действительно переданы «по ссылке», то указатель на место в памяти этой переменной будет передаваемой вещью, а не само значение. И если вы передадите указатель (т. Е. Адрес памяти), то любые сделанные изменения будут немедленно отражены, даже если следующая строка хранимой процедуры вызывает ошибку и прерывает выполнение.
Подводя итог, часть 1: значения переменных всегда копируются; на них не ссылается их адрес памяти.
С учетом части 1 политика постоянного копирования значений переменных может привести к проблемам с ресурсами, когда передаваемая переменная достаточно велика. Я не проверял , чтобы видеть , как типы BLOB обрабатываются ( VARCHAR(MAX)
, NVARCHAR(MAX)
, VARBINARY(MAX)
, XML
, и те , которые не должны больше использоваться: TEXT
, NTEXT
и IMAGE
), но с уверенностью можно сказать , что любая таблица данных, передаваемых в может быть довольно большим. Для тех, кто разрабатывает функцию TVP, имело бы смысл иметь истинную возможность «передачи по ссылке», чтобы их новая классная функция не разрушила здоровое количество систем (т.е. хотел бы иметь более масштабируемый подход). Как вы можете видеть в документации , вот что они сделали:
Transact-SQL передает табличные параметры в подпрограммы по ссылке, чтобы избежать копирования входных данных.
Кроме того, эта проблема управления памятью не была новой концепцией, поскольку ее можно найти в API SQLCLR, который был представлен в SQL Server 2005 (TVP были введены в SQL Server 2008). При передаче NVARCHAR
и VARBINARY
данных в код SQLCLR (т. Е. Входные параметры для методов .NET в сборке SQLCLR) у вас есть возможность перейти к подходу "по значению", используя один SqlString
или SqlBinary
соответственно, или вы можете перейти к "по ссылке «подход с использованием либо SqlChars
или SqlBytes
соответственно. SqlChars
И SqlBytes
типы позволяют полностью стриминг данных в .NET CLR, что вы можете тянуть небольшие куски больших значений , в отличие от копирования всего 200 МБ (до 2 Гб, справа) значения.
Подводя итог, можно сказать, что часть 2: TVP по своей природе будут иметь склонность потреблять много памяти (и, следовательно, ухудшать производительность), если останутся в рамках модели «всегда копировать значение». Следовательно, TVP делают «передачу по ссылке».
Последняя часть - то, почему Часть 2 имеет значение: почему передача TVP действительно «по ссылке» вместо того, чтобы делать копию этого, что-то меняет. И на это отвечает цель разработки, которая является основой для Части 1: Хранимые процедуры, которые не завершаются успешно, никоим образом не должны изменять любые входные параметры, независимо от того, помечены они как OUTPUT
или нет. Разрешение операций DML будет иметь непосредственное влияние на значение TVP, поскольку оно существует в вызывающем контексте (поскольку передача по ссылке означает, что вы меняете то, что было передано, а не копию того, что было передано).
Теперь кто-то где-то в этот момент, вероятно, разговаривает со своим монитором и говорит: «Ну, просто встроите автоматическое средство для отката любых изменений, внесенных в параметры TVP, если они были переданы в хранимую процедуру. Дух. Проблема решена». Не так быстро. Вот где возникает природа табличных переменных: изменения, вносимые в переменные таблицы, не связаны транзакциями! Так что нет возможности откатить изменения обратно. И на самом деле, это трюк, используемый для сохранения информации, сгенерированной в транзакции, если требуется откат :-).
Подводя итог: Часть 3. Табличные переменные не допускают «отмены» внесенных в них изменений в случае ошибки, которая приводит к прерыванию хранимой процедуры. И это нарушает цель разработки, состоящую в том, чтобы параметры никогда не отражали частичное выполнение (часть 1).
TYPE
переменную TVP или aDECLARE x as TABLE (...)
) с помощью хранимой процедуры? Могу ли я сделать это, хотя и с большим объемом памяти, с помощью функции вместо,set @tvp = myfunction(@tvp)
если значение моей функцииRETURNS
- это таблица с тем же DDL, что и тип TVP?SET
табличной переменной, по крайней мере, я не знаю. И даже если бы вы могли: а) вы не можете получить доступ к результирующему набору через=
оператора, и б) TVP все еще помечен какREADONLY
, поэтому его установка нарушит это. Просто выгрузите содержимое во временную таблицу или другую переменную таблицы, которую вы создаете в proc.Ответ сообщества Wiki, созданный на основе комментария к вопросу Мартина Смита
Для этого есть активный элемент Connect (предоставленный Erland Sommarskog):
Ослабьте ограничение, что параметры таблицы должны быть доступны только для чтения, когда SP вызывают друг друга
Единственный ответ Microsoft до сих пор говорит (выделение добавлено):
источник