Wyjaśnienie wydaje się być powiązane z kombinacją: a) szczegółu z połączonego bloga, który nie został wspomniany w tym pytaniu, b) pragmatyki dopasowania TVP do sposobu, w jaki parametry zawsze były przekazywane i wychodzące, c) i natury zmiennych tabeli.
Brakujący szczegół zawarty w łączonym poście na blogu to dokładnie sposób, w jaki zmienne są przekazywane i wyprowadzane z przechowywanych procedur i funkcji (co odnosi się do sformułowania w pytaniu „bezpieczniejsza wersja przekazywania przez odniesienie, jeśli są parametrami WYJŚCIA”) :
TSQL używa semantyki kopiowania / kopiowania do przekazywania parametrów do procedur przechowywanych i funkcji ....
... gdy zapisany proces zakończy wykonywanie (bez popełniania błędu), wykonywana jest kopia, która aktualizuje przekazany parametr o wszelkie zmiany, które zostały wprowadzone w zapisanym proc.
Prawdziwą zaletą tego podejścia jest przypadek błędu. Jeśli błąd wystąpi w trakcie wykonywania procedury składowanej, wszelkie zmiany dokonane w parametrach nie zostaną przeniesione z powrotem do programu wywołującego.
Jeśli słowo kluczowe WYJŚCIE nie jest obecne, nie jest wykonywane kopiowanie.
Podsumowując:
Parametry do przechowywanych proc nigdy nie odzwierciedlają częściowego wykonania zapisanego proc, jeśli napotkał błąd.
Część 1 tej układanki polega na tym, że parametry są zawsze przekazywane „przez wartość”. I dopiero wtedy, gdy parametr zostanie oznaczony jako OUTPUT
i procedura składowana zakończy się powodzeniem, bieżąca wartość zostanie faktycznie odesłana. Gdyby OUTPUT
wartości były rzeczywiście przekazywane „przez odniesienie”, to wskaźnik do lokalizacji w pamięci tej zmiennej byłby rzeczą, która została przekazana, a nie samą wartością. A jeśli podasz wskaźnik (tj. Adres pamięci), wszelkie wprowadzone zmiany zostaną natychmiast odzwierciedlone, nawet jeśli następny wiersz Procedury składowanej spowoduje błąd i przerwie wykonywanie.
Podsumowując Część 1: wartości zmiennych są zawsze kopiowane; nie są przywoływane przez ich adres pamięci.
Mając na uwadze Część 1, zasada zawsze kopiowania wartości zmiennych może prowadzić do problemów z zasobami, gdy przekazywana zmienna jest dość duża. I nie zostało przetestowane, aby zobaczyć, jak są obsługiwane typy blob ( VARCHAR(MAX)
, NVARCHAR(MAX)
, VARBINARY(MAX)
, XML
, i te, które nie powinny być stosowane już: TEXT
, NTEXT
, i IMAGE
), ale jest to na pewno powiedzieć, że każda tabela danych są przekazywane w może być dość duża. Rozsądnym byłoby, aby osoby opracowujące funkcję TVP pragnęły prawdziwej zdolności „przejścia przez odniesienie”, aby zapobiec ich nowej fajnej funkcji niszczenia zdrowej liczby systemów (tj. Chcieć bardziej skalowalnego podejścia). Jak widać w dokumentacji , zrobili to:
Transact-SQL przekazuje parametry o wartościach przechowywanych w tabeli do procedur przez odwołanie, aby uniknąć tworzenia kopii danych wejściowych.
Ponadto problem zarządzania pamięcią nie był nową koncepcją, ponieważ można go znaleźć w interfejsie API SQLCLR, który został wprowadzony w SQL Server 2005 (TVP zostały wprowadzone w SQL Server 2008). Po przejściu NVARCHAR
i VARBINARY
dane do kodu SQLCLR (tj parametrów wejściowych na metodach NET w ramach Zgromadzenia SQLCLR), masz możliwość, aby przejść z podejściem „wartością” za pomocą jednej SqlString
lub SqlBinary
odpowiednio, czy można przejść z „przez odniesienie „podejście przy użyciu odpowiednio jednego SqlChars
lub SqlBytes
drugiego. SqlChars
I SqlBytes
typy pozwalają na pełną streaming danych do .NET CLR takie, że można wyciągnąć małe kawałki dużych wartościach w przeciwieństwie do kopiowania całe 200 MB (do 2 GB, prawy) wartość.
Podsumowując Część 2: TVP ze swojej natury mieliby skłonność do konsumowania dużej ilości pamięci (a tym samym pogorszenia wydajności), jeśli pozostaliby w modelu „zawsze kopiuj wartość”. Dlatego TVP robią prawdziwy „przekaz przez odniesienie”.
Ostatnia część jest tak ważna, ponieważ część 2 ma znaczenie: dlaczego przekazanie TVP naprawdę „przez odniesienie”, zamiast robić jego kopię, by cokolwiek zmienić. Odpowiada na to cel projektu, który jest podstawą Części 1: Procedury składowane, które nie zostały pomyślnie ukończone, nie powinny w żaden sposób zmieniać żadnego z parametrów wejściowych, niezależnie od tego, czy są one oznaczone jako OUTPUT
czy nie. Zezwolenie na operacje DML miałoby bezpośredni wpływ na wartość TVP, ponieważ istnieje ona w kontekście wywoływania (ponieważ przekazywanie przez referencję oznacza zmianę rzeczy, która została przekazana, a nie kopię tego, co zostało przekazane).
Teraz ktoś gdzieś w tym miejscu prawdopodobnie rozmawia ze swoim monitorem, mówiąc: „Cóż, po prostu zbuduj narzędzie do automatyzacji, aby cofnąć wszelkie zmiany dokonane w parametrach TVP, jeśli zostały wprowadzone do Procedury składowanej. Cóż. Problem rozwiązany”. Nie tak szybko. Tu właśnie pojawia się natura Zmiennych Tabeli: zmiany wprowadzone w Zmiennych Tabeli nie są związane Transakcjami! Nie ma więc możliwości wycofania zmian. I w rzeczywistości jest to sztuczka używana do zapisywania informacji generowanych w ramach transakcji, jeśli konieczne jest wycofanie :-).
Podsumowując Część 3: Zmienne tabelowe nie pozwalają na „cofanie” wprowadzonych do nich zmian w przypadku błędu, który powoduje przerwanie procedury składowanej. I to narusza cel projektowy, aby parametry nigdy nie odzwierciedlały częściowego wykonania (Część 1).
TYPE
zmienną TVP użytkownika lub aDECLARE x as TABLE (...)
) za pomocą procedury składowanej? Czy mogę to zrobić, choć z większym wykorzystaniem pamięci, z funkcją zamiast tego,set @tvp = myfunction(@tvp)
jeśli wartością mojej funkcjiRETURNS
jest tabela z tym samym DDL co typ TVP?