Пояснення, схоже, пов'язане з поєднанням: а) деталей із пов’язаного блогу, про який не згадувалося в цьому запитанні; б) прагматики ТВП, що підходить до того, як параметри завжди передавались і виходили, в) та природи табличних змінних.
Пропущена деталь, що міститься у пов’язаному дописі блогу, полягає саме в тому, як передаються змінні та не зберігаються збережені процедури та функції (що стосується фразування у запитанні "більш безпечної версії пропускного посилання, якщо вони є параметрами OUTPUT") :
TSQL використовує семантику копіювання / копіювання для передачі параметрів у збережені процедури та функції ....
... коли збережений proc закінчує виконувати (не вказуючи на помилку), робиться копія, яка оновлює переданий параметр з будь-якими змінами, які були внесені до нього у збереженій програмі.
Справжня користь такого підходу полягає у випадку помилки. Якщо в середині виконання збереженої процедури виникає помилка, будь-які зміни, внесені до параметрів, не повернуться до абонента.
Якщо ключового слова OUTPUT немає, копіювання не робиться.
Суть:
Параметри для збережених програм ніколи не відображають часткове виконання збереженої програми, якщо в ній виникли помилки.
Частина 1 цієї головоломки полягає в тому, що параметри завжди передаються "за значенням". І лише тоді, коли параметр позначений як 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 має значення: чому передача телевізора по-справжньому "за посиланням" замість того, щоб робити його копію, що-небудь змінила. І на це відповідає мета проектування, яка є основою для Частини 1: Збережені процедури, які не завершуються успішно, не повинні жодним чином змінювати будь-які вхідні параметри, незалежно від того, позначені вони як OUTPUT
ні. Дозвіл операцій DML негайно вплине на значення TVP, яке воно існує в контексті виклику (оскільки пройшовши посиланням, ви змінюєте те, що було передано, а не копію того, що було передано).
Зараз, хтось десь, можливо, в цей момент, можливо, розмовляє зі своїм монітором, кажучи: "Ну, просто побудуйте автоматичний засіб для відкату будь-яких змін, внесених до параметрів TVP, якщо такі були передані в процедуру збереження. Дух. Проблема вирішена". Не так швидко. Ось тут і походить природа табличних змінних: зміни, внесені до змінних таблиць, не пов'язані транзакціями! Тому немає можливості повернути зміни. Насправді це трюк, який використовується для збереження інформації, сформованої в рамках транзакції, якщо потрібно відкинути :-).
Підсумовуючи частину 3: Змінні таблиці не дозволяють "скасовувати" зміни, внесені до них у випадку помилки, яка спричиняє переривання збереженої процедури. І це порушує проектну мету, щоб параметри ніколи не відображали часткове виконання (частина 1).
TYPE
змінну користувача TVP або aDECLARE x as TABLE (...)
) із збереженою процедурою? Чи можу я це зробити, хоча і з більшим слідом пам'яті, але замість функції,set @tvp = myfunction(@tvp)
якщо значенням моєї функціїRETURNS
є таблиця з тим же DDL, що і тип TVP?