L'explication semble être liée à une combinaison de: a) un détail du blog lié qui n'a pas été mentionné dans cette question, b) la pragmatique des TVP s'inscrivant dans la façon dont les paramètres ont toujours été entrés et sortis, c) et la nature des variables de table.
Le détail manquant contenu dans le billet de blog lié est exactement la façon dont les variables sont transmises dans et hors des procédures et fonctions stockées (qui se rapportent à la formulation dans la question "d'une version plus sûre de la référence par contournement si ce sont des paramètres de SORTIE") :
TSQL utilise une sémantique de copie / copie pour transmettre les paramètres aux procédures et fonctions stockées ....
... lorsque le processus stocké termine son exécution (sans générer d'erreur), une copie est effectuée qui met à jour le paramètre transmis avec toutes les modifications qui lui ont été apportées dans le processus stocké.
Le véritable avantage de cette approche est dans le cas d'erreur. Si une erreur se produit au milieu de l'exécution d'une procédure stockée, les modifications apportées aux paramètres ne se propageront pas à l'appelant.
Si le mot clé OUTPUT n'est pas présent, aucune copie n'est effectuée.
Conclusion : les paramètres des proc stockés ne reflètent jamais l'exécution partielle du proc stocké en cas d'erreur.
La partie 1 de ce puzzle est que les paramètres sont toujours passés "par valeur". Et, ce n'est que lorsque le paramètre est marqué comme OUTPUT
et que la procédure stockée se termine avec succès que la valeur actuelle est effectivement renvoyée. Si les OUTPUT
valeurs étaient vraiment passées "par référence", alors le pointeur vers l'emplacement en mémoire de cette variable serait la chose qui a été passée, pas la valeur elle-même. Et si vous passez le pointeur (c'est-à-dire l'adresse mémoire), toutes les modifications apportées sont immédiatement reflétées, même si la ligne suivante de la procédure stockée provoque une erreur et abandonne l'exécution.
Pour résumer la partie 1: les valeurs des variables sont toujours copiées; ils ne sont pas référencés par leur adresse mémoire.
Avec la partie 1 à l'esprit, une politique de toujours copier les valeurs des variables peut entraîner des problèmes de ressources lorsque la variable transmise est assez grande. Je ne l' ai pas testé pour voir comment les types blob sont traités ( VARCHAR(MAX)
, NVARCHAR(MAX)
, VARBINARY(MAX)
, XML
et ceux qui ne doivent pas être utilisés plus: TEXT
, NTEXT
et IMAGE
), mais il est sûr de dire que toutes les tables de données passées en pourrait être assez grand. Il serait logique pour ceux qui développent la fonctionnalité TVP de désirer une véritable capacité de "passage par référence" pour empêcher leur nouvelle fonctionnalité cool de détruire un nombre sain de systèmes (c'est-à-dire de vouloir une approche plus évolutive). Comme vous pouvez le voir dans la documentation, c'est ce qu'ils ont fait:
Transact-SQL transmet les paramètres de table aux routines par référence pour éviter de faire une copie des données d'entrée.
En outre, ce problème de gestion de la mémoire n'était pas un nouveau concept car il peut être trouvé dans l'API SQLCLR qui a été introduite dans SQL Server 2005 (les TVP ont été introduits dans SQL Server 2008). Lors du passage NVARCHAR
et des VARBINARY
données dans le code SQLCLR (c'est-à-dire les paramètres d'entrée sur les méthodes .NET dans un assemblage SQLCLR), vous avez la possibilité de suivre l'approche "par valeur" en utilisant soit SqlString
ou SqlBinary
respectivement, soit vous pouvez utiliser la "par référence" "approche en utilisant respectivement SqlChars
ou SqlBytes
. Les types SqlChars
et SqlBytes
permettent une diffusion complète des données dans le .NET CLR de sorte que vous pouvez extraire de petits morceaux de grandes valeurs au lieu de copier une valeur entière de 200 Mo (jusqu'à 2 Go, à droite).
Pour résumer la partie 2: les TVP, de par leur nature même, auraient une propension à consommer beaucoup de mémoire (et donc à détériorer les performances) s'ils restent dans le modèle "toujours copier la valeur". Les TVP font donc un véritable "pass by reference".
La dernière pièce est pourquoi la partie 2 est importante: pourquoi le fait de passer un TVP vraiment "par référence" au lieu d'en faire une copie changerait quoi que ce soit. Et cela est répondu par l'objectif de conception qui est à la base de la partie 1: les procédures stockées qui ne se terminent pas avec succès ne doivent en aucun cas modifier les paramètres d'entrée, qu'ils soient marqués OUTPUT
ou non. Autoriser les opérations DML aurait un effet immédiat sur la valeur du TVP tel qu'il existe dans le contexte d'appel (car le passage par référence signifie que vous changez la chose qui a été transmise, pas une copie de ce qui a été transmis).
Maintenant, quelqu'un, quelque part, parle probablement à son moniteur à ce stade, "Eh bien, il suffit de construire une installation automagique pour annuler toutes les modifications apportées aux paramètres TVP si elles étaient transmises à la procédure stockée. Duh. Le problème est résolu." Pas si vite. C'est là que la nature des variables de tableau entre en jeu: les modifications apportées aux variables de tableau ne sont pas liées par les transactions! Il n'y a donc aucun moyen d'annuler les modifications. Et en fait, c'est une astuce utilisée pour enregistrer les informations générées dans une transaction s'il doit y avoir un retour en arrière :-).
Pour résumer la partie 3: les variables de table ne permettent pas d'annuler les modifications qui leur sont apportées en cas d'erreur provoquant l'abandon de la procédure stockée. Et cela viole l'objectif de conception d'avoir des paramètres ne reflétant jamais l'exécution partielle (Partie 1).
Ergo: le READONLY
mot-clé est nécessaire pour empêcher les opérations DML sur les TVP car ce sont des variables de table qui sont réellement passées "par référence", et donc toute modification de celles-ci serait immédiatement reflétée, même si la procédure stockée rencontre une erreur, et il n'y a pas autre moyen d'empêcher cela.
De plus, les paramètres d'autres types de données ne peuvent pas être utilisés READONLY
car ils sont déjà des copies de ce qui a été transmis, et ne protègent donc rien qui ne soit déjà protégé. Cela, et la façon dont les paramètres des autres types de données fonctionnent étaient destinés à être en lecture-écriture, il serait donc probablement encore plus difficile de modifier cette API pour inclure maintenant un concept en lecture seule.
TYPE
variable TVP utilisateur ou aDECLARE x as TABLE (...)
) avec une procédure stockée? Puis-je le faire, bien qu'avec une plus grande empreinte mémoire, avec une fonction à la place avecset @tvp = myfunction(@tvp)
si laRETURNS
valeur de ma fonction est une table avec le même DDL que le type TVP?SET
une variable de table, du moins pas que je sache. Et même si vous pouviez: a) vous ne pouvez pas accéder à un jeu de résultats via l'=
opérateur, et b) le TVP est toujours marqué commeREADONLY
, donc le définir violerait cela. Vider simplement le contenu dans une table temporaire ou une autre variable de table que vous créez dans le proc.Réponse Wiki communautaire générée à partir d'un commentaire sur la question par Martin Smith
Il existe un élément Connect actif (soumis par Erland Sommarskog) pour cela:
Relâchez la restriction selon laquelle les paramètres de table doivent être en lecture seule lorsque les SP s'appellent les uns les autres
La seule réponse de Microsoft à ce jour dit (c'est moi qui souligne):
la source