web-dev-qa-db-fra.com

Pourquoi les TVP doivent-ils être en lecture seule, et pourquoi les paramètres des autres types ne peuvent-ils pas être en lecture seule

Selon ce blog les paramètres d'une fonction ou d'une procédure stockée sont essentiellement des valeurs de passage s'ils ne sont pas des paramètres OUTPUT, et sont essentiellement traités comme une version plus sûre du passage -référence si ce sont des paramètres OUTPUT.

Au début, je pensais que le fait de forcer TVP à être déclaré READONLY était de signaler clairement aux développeurs que le TVP ne peut pas être utilisé comme paramètre OUTPUT, mais il doit y en avoir plus car nous ne pouvons pas déclarer non-TVP comme READONLY. Par exemple, ce qui suit échoue:

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

Msg 346, niveau 15, état 1, test de procédure
Le paramètre "@a" ne peut pas être déclaré LECTURE car il ne s'agit pas d'un paramètre table.

  1. Depuis les statistiques ne sont pas stockées sur TVP quelle est la raison derrière la prévention des opérations DML?
  2. Est-ce lié à ne pas vouloir que TVP soit OUTPUT paramètres pour une raison quelconque?
19
Erik

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.

  1. 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 de "une version plus sûre de la transmission par référence 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 proc stocké termine son exécution (sans rencontrer 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 proc 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, toute modification apportée aux paramètres ne se propagera pas à l'appelant.

    Si le mot clé OUTPUT n'est pas présent, aucune copie n'est effectuée.

    La ligne du bas:
    Les paramètres des proc stockés ne reflètent jamais l'exécution partielle du proc stocké s'il a rencontré une 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 valeurs OUTPUT é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.

  2. 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 n'ai pas testé pour voir comment les types d'objets blob sont géré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 toute table de données transmise pourrait être assez grande. 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). Lorsque vous passez des données NVARCHAR et VARBINARY dans le code SQLCLR (c'est-à-dire des paramètres d'entrée sur les méthodes .NET dans un assembly SQLCLR), vous avez la possibilité de suivre l'approche "par valeur" en utilisant soit SqlString ou SqlBinary respectivement, ou vous pouvez utiliser l'approche "par référence" 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 200 Mo entiers (jusqu'à 2 Go , à droite) valeur.

    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".

  3. 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 correctement ne doivent en aucun cas modifier les paramètres d'entrée, qu'ils soient marqués comme 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, construisez simplement une installation automagique pour annuler toutes les modifications apportées aux paramètres TVP si elles ont été transmises à la procédure stockée. Duh. Problème 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 dans le cas d'une erreur entraînant 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 mot clé READONLY 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 par conséquent, 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 aucun autre moyen de l'empêcher.

De plus, les paramètres d'autres types de données ne peuvent pas utiliser 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.

19
Solomon Rutzky

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:

Assouplissez 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):

Merci pour les commentaires à ce sujet. Nous avons reçu des commentaires similaires d'un grand nombre de clients. Autoriser la lecture/écriture des paramètres de valeur de la table implique beaucoup de travail du côté du moteur SQL ainsi que des protocoles clients. En raison de contraintes de temps/de ressources ainsi que d'autres priorités, nous ne pourrons pas reprendre ce travail dans le cadre de la version SQL Server 2008. Cependant, nous avons étudié ce problème et l'avons fermement dans notre radar pour le résoudre dans le cadre de la prochaine version de SQL Server. Nous apprécions et apprécions les commentaires ici.

Srini Acharya
Gestionnaire principal de programme
Moteur relationnel SQL Server

5
Paul White 9