Чому ТВП повинні бути ГОТОВИМИ, а чому параметри інших типів не можуть бути ГОТОВИМИ


19

Відповідно до цього блогу параметри функції або збереженої процедури, по суті, є прохідними за значенням, якщо вони не є OUTPUTпараметрами, і по суті трактуються як більш безпечна версія передачі посилання, якщо вони є OUTPUTпараметрами.

Спочатку я подумав, що метою змусити оголосити TVP READONLYбуло чітко сигналізувати розробникам про те, що TVP не може використовуватися як OUTPUTпараметр, але потрібно робити більше, тому що ми не можемо оголосити не-TVP як READONLY. Наприклад, такі помилки:

create procedure [dbo].[test]
@a int readonly
as
    select @a

346, рівень 15, стан 1, тест процедури
Параметр "@a" не можна оголосити НАЙКРАЙНО, оскільки він не є табличним параметром.

  1. Оскільки статистика не зберігається на TVP, що обґрунтовує запобігання операцій DML?
  2. Це пов’язано з тим, що OUTPUTчомусь не потрібно, щоб TVP був параметром?

Відповіді:


19

Пояснення, схоже, пов'язане з поєднанням: а) деталей із пов’язаного блогу, про який не згадувалося в цьому запитанні; б) прагматики ТВП, що підходить до того, як параметри завжди передавались і виходили, в) та природи табличних змінних.

  1. Пропущена деталь, що міститься у пов’язаному дописі блогу, полягає саме в тому, як передаються змінні та не зберігаються збережені процедури та функції (що стосується фразування у запитанні "більш безпечної версії пропускного посилання, якщо вони є параметрами OUTPUT") :

    TSQL використовує семантику копіювання / копіювання для передачі параметрів у збережені процедури та функції ....

    ... коли збережений proc закінчує виконувати (не вказуючи на помилку), робиться копія, яка оновлює переданий параметр з будь-якими змінами, які були внесені до нього у збереженій програмі.

    Справжня користь такого підходу полягає у випадку помилки. Якщо в середині виконання збереженої процедури виникає помилка, будь-які зміни, внесені до параметрів, не повернуться до абонента.

    Якщо ключового слова OUTPUT немає, копіювання не робиться.

    Суть:
    Параметри для збережених програм ніколи не відображають часткове виконання збереженої програми, якщо в ній виникли помилки.

    Частина 1 цієї головоломки полягає в тому, що параметри завжди передаються "за значенням". І лише тоді, коли параметр позначений як OUTPUT і Збережена процедура завершиться успішно, поточне значення фактично відправляється назад. Якби OUTPUTзначення були справді передані "за посиланням", то вказівник на розташування в пам'яті цієї змінної був би переданою річчю, а не самим значенням. І якщо ви перейдете в покажчик (тобто адреса пам’яті), то будь-які внесені зміни негайно відображаються, навіть якщо наступний рядок збереженої процедури викликає помилку, і вона припиняє виконання.

    Підсумовуючи частину 1: змінні значення завжди копіюються; на них не посилається їх адреса пам'яті.

  2. Зважаючи на частину 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 роблять справжній "пропуск посилання".

  3. Останній фрагмент - чому частина 2 має значення: чому передача телевізора по-справжньому "за посиланням" замість того, щоб робити його копію, що-небудь змінила. І на це відповідає мета проектування, яка є основою для Частини 1: Збережені процедури, які не завершуються успішно, не повинні жодним чином змінювати будь-які вхідні параметри, незалежно від того, позначені вони як OUTPUTні. Дозвіл операцій DML негайно вплине на значення TVP, яке воно існує в контексті виклику (оскільки пройшовши посиланням, ви змінюєте те, що було передано, а не копію того, що було передано).

    Зараз, хтось десь, можливо, в цей момент, можливо, розмовляє зі своїм монітором, кажучи: "Ну, просто побудуйте автоматичний засіб для відкату будь-яких змін, внесених до параметрів TVP, якщо такі були передані в процедуру збереження. Дух. Проблема вирішена". Не так швидко. Ось тут і походить природа табличних змінних: зміни, внесені до змінних таблиць, не пов'язані транзакціями! Тому немає можливості повернути зміни. Насправді це трюк, який використовується для збереження інформації, сформованої в рамках транзакції, якщо потрібно відкинути :-).

    Підсумовуючи частину 3: Змінні таблиці не дозволяють "скасовувати" зміни, внесені до них у випадку помилки, яка спричиняє переривання збереженої процедури. І це порушує проектну мету, щоб параметри ніколи не відображали часткове виконання (частина 1).

Ergo:READONLY ключове слово не потрібно , щоб запобігти операції DML на TVPs , так як вони є табличний змінними, які на насправді пройшли «по посиланню», і , отже , будь-які зміни в них будуть негайно відображатися, навіть якщо збережена процедура виявляє помилку, і немає інший спосіб запобігти цьому.

Крім того, параметри інших типів даних не можуть використовуватись, READONLYоскільки вони вже є копіями переданих даних, і тому це не захистить нічого, що вже не захищено. Це та спосіб, яким параметри інших типів даних призначені для читання-запису, тому, ймовірно, буде ще більше роботи над зміною цього API, щоб він тепер включав концепцію лише для читання.


Дуже детальне пояснення. Спасибі. Отже, немає способу змінити змінну переданої таблиці (або TYPEзмінну користувача TVP або a DECLARE x as TABLE (...)) із збереженою процедурою? Чи можу я це зробити, хоча і з більшим слідом пам'яті, але замість функції, set @tvp = myfunction(@tvp)якщо значенням моєї функції RETURNSє таблиця з тим же DDL, що і тип TVP?
mpag

@mpag Спасибі TVP - це таблична змінна, різниці немає. Ви не передаєте тип, передаєте змінну таблиці, створену з типу або з явного оголошення схеми. Крім того, ви не можете змінити SETтаблицю, принаймні не те, що мені відомо. І навіть якщо ви могли б: а) ви не можете отримати доступ до набору результатів через =оператора, і б) TVP все ще позначений як READONLY, тож встановлення це порушило б це. Просто скиньте вміст у тимчасову таблицю або іншу змінну таблиці, яку ви створюєте у програмі.
Соломон Руцький

Знову дякую. Я вирішив по суті використовувати підхід до темп-таблиць.
mpag

5

Відповідь Вікі спільноти отримана з коментаря до запитання Мартіна Сміта

Для цього є активний елемент Connect (наданий Ерланом Соммарським) для цього:

Розслабте обмеження, що параметри таблиці необхідно зчитувати лише тоді, коли SP-адреси дзвонять один одному

Єдиний відповідь Microsoft поки що говорить (акцент додано):

Дякуємо за відгук про це. Ми отримали подібні відгуки від великої кількості клієнтів. Дозвіл для читання / запису оцінених параметрів таблиці передбачає чималу роботу на стороні SQL Engine, а також клієнтських протоколів. Через обмеження часу та ресурсів, а також інших пріоритетів, ми не зможемо взятися за цю роботу як частину випуску SQL Server 2008. Однак ми дослідили цю проблему і вирішили вирішити цю проблему як частину наступного випуску SQL Server. Ми цінуємо і вітаємо відгуки тут.

Шріні Ачарія,
старший менеджер програм
реляційного двигуна SQL Server

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.