Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Msg 6522, Avertissement de niveau 16 lors de l'exécution de la procédure stockée clr

Il y a plusieurs problèmes dans ce code qui doivent être résolus :

  1. Concernant la question posée, lorsque vous obtenez une System.Security.SecurityException erreur, qui fait référence au code essayant d'atteindre l'extérieur de la base de données, quelque chose qui n'est pas autorisé dans un SAFE Assemblée. La façon dont vous résolvez ce problème dépend de ce que vous essayez d'accomplir.

    • Si vous essayez d'accéder au système de fichiers, de lire à partir du registre, d'obtenir une variable d'environnement, d'accéder au réseau pour une connexion non-SQL Server (par exemple, http, ftp), etc., l'assembly a besoin d'un PERMISSION_SET de EXTERNAL_ACCESS . Afin de définir votre assemblage sur autre chose que SAFE , vous devez soit :
      • Créez un certificat ou une clé asymétrique basé sur la même clé que vous avez utilisée pour signer votre assemblage (c'est-à-dire donnez-lui un nom fort), créez une connexion basée sur ce certificat ou cette clé asymétrique, puis accordez le EXTERNAL ACCESS ASSEMBLY l'autorisation à cette connexion. Cette méthode est grandement préféré à l'autre méthode, qui est :
      • Définir la base de données contenant l'assembly sur TRUSTWORTHY ON . Cette méthode ne doit être utilisée qu'en dernier recours s'il n'est pas possible de signer l'assembly. Ou à des fins de test rapide. Définition d'une base de données sur TRUSTWORTHY ON ouvre votre instance à des menaces de sécurité potentielles et doit être évitée, même si elle est plus rapide/simple que l'autre méthode.
    • Si vous essayez d'accéder à l'instance SQL Server à laquelle vous êtes déjà connecté, vous avez la possibilité d'utiliser la connexion en cours de Context Connection = true; qui peut être fait dans un SAFE Assemblée. C'est ce que @Marc a suggéré dans sa réponse. Bien qu'il y ait certainement des avantages à utiliser ce type de connexion, et bien que la connexion contextuelle soit le choix approprié dans ce scénario particulier, il est trop simpliste et incorrect de dire que vous devriez toujours utiliser ce type de connexion. Examinons les aspects positifs et négatifs de la connexion contextuelle :

      • Points positifs :
        • Peut être fait dans un SAFE assemblage.
        • Très faible surcharge de connexion, voire aucune, car il ne s'agit pas d'une connexion supplémentaire.
        • Fait partie de la session en cours afin que tout SQL que vous exécutez ait accès aux éléments basés sur la session tels que les tables temporaires locales et CONTEXT_INFO .
      • Négatifs :

        • Ne peut pas être utilisé si l'emprunt d'identité a été activé.
        • Ne peut se connecter qu'à l'instance SQL Server actuelle.
        • Lorsqu'il est utilisé dans Functions (Scalar et Table-Valued), il présente les mêmes restrictions que les fonctions T-SQL (par exemple, aucune opération secondaire n'est autorisée), sauf que vous pouvez exécuter des procédures stockées en lecture seule.
        • Les fonctions de table ne sont pas autorisées à diffuser leurs résultats si elles lisent un ensemble de résultats.

        Tous ces "négatifs" sont autorisés lors de l'utilisation d'une connexion régulière/externe, même s'il s'agit de la même instance à partir de laquelle vous exécutez ce code.

  2. Si vous vous connectez à l'instance à partir de laquelle vous exécutez ce code et que vous utilisez une connexion externe/régulière, il n'est pas nécessaire de spécifier le nom du serveur ou même d'utiliser localhost . La syntaxe préférée est Server = (local) qui utilise la mémoire partagée alors que les autres utilisent parfois TCP/IP qui n'est pas aussi efficace.

  3. À moins que vous n'ayez une raison très précise de le faire, n'utilisez pas Persist Security Info=True;

  4. C'est une bonne pratique de Dispose() de votre SqlCommand

  5. Il est plus efficace d'appeler le insertcommand.Parameters.Add() juste avant le for boucle, puis à l'intérieur de la boucle, définissez simplement la valeur via firstname.Value = , ce que vous faites déjà, il suffit donc de déplacer le insertcommand.Parameters.Add() lignes juste avant le for ligne.

  6. tel / @tel / listtelnumber sont INT au lieu de VARCHAR / string . Les numéros de téléphone, tout comme les codes postaux et les numéros de sécurité sociale (SSN), ne sont pas chiffres, même s'ils semblent l'être. INT impossible de stocker le 0 de début s ou quelque chose comme ex. pour signifier une "extension".

  7. Tout cela étant dit, même si tout ce qui précède est corrigé, il y a toujours un énorme problème avec ce code qui devrait être résolu :il s'agit d'une opération plutôt simpliste à effectuer en T-SQL pur, et le faire en SQLCLR est trop compliqué, plus difficile et plus coûteux à maintenir, et beaucoup plus lent. Ce code effectue 10 000 transactions distinctes alors qu'il pourrait si facilement être fait comme une seule requête basée sur un ensemble (c'est-à-dire une transaction). Vous pouvez envelopper votre for boucle dans une transaction qui l'accélérerait, mais elle sera toujours plus lente que l'approche T-SQL basée sur les ensembles car elle doit toujours émettre 10 000 INSERT distincts déclarations. Vous pouvez facilement randomiser dans T-SQL en utilisant soit NEWID() ou CRYPT_GEN_RANDOM qui a été introduit dans SQL Server 2008. (veuillez consulter la MISE À JOUR ci-dessous)

Si vous souhaitez en savoir plus sur SQLCLR, veuillez consulter la série que j'écris pour SQL Server Central : Escalier vers SQLCLR (inscription gratuite requise).

MISE À JOUR

Voici une pure méthode T-SQL pour générer ces données aléatoires, en utilisant les valeurs de la Question. Il est facile d'ajouter de nouvelles valeurs à l'une des 4 variables de table (pour augmenter le nombre de combinaisons possibles) car la requête ajuste dynamiquement la plage de randomisation pour s'adapter aux données de chaque variable de table (c'est-à-dire les lignes 1 à n).

DECLARE @TelNumber TABLE (TelNumberID INT NOT NULL IDENTITY(1, 1),
                          Num VARCHAR(30) NOT NULL);
INSERT INTO @TelNumber (Num) VALUES ('1525407'), ('5423986'), ('1245398'), ('32657891'),
                                    ('123658974'), ('7896534'), ('12354698');

DECLARE @FirstName TABLE (FirstNameID INT NOT NULL IDENTITY(1, 1),
                          Name NVARCHAR(30) NOT NULL);
INSERT INTO @FirstName (Name) VALUES ('Babak'), ('Carolin'), ('Martin'), ('Marie'),
                  ('Susane'), ('Michail'), ('Ramona'), ('Ulf'), ('Dirk'), ('Sebastian');

DECLARE @LastName TABLE (LastNameID INT NOT NULL IDENTITY(1, 1),
                         Name NVARCHAR(30) NOT NULL);
INSERT INTO @LastName (Name) VALUES ('Bastan'), ('Krause'), ('Rosner'),
                  ('Gartenmeister'), ('Rentsch'), ('Benn'), ('Kycik'), ('Leuoth'),
                  ('Kamkar'), ('Kolaee');

DECLARE @Address TABLE (AddressID INT NOT NULL IDENTITY(1, 1),
                        Addr NVARCHAR(100) NOT NULL);
INSERT INTO @Address (Addr) VALUES ('Deutschlan Chemnitz Sonnenstraße 59'), (''),
  ('Deutschland Chemnitz Arthur-Strobel straße 124'),
  ('Deutschland Chemnitz Brückenstraße 3'),
  ('Iran Shiraz Chamran Blvd, Niayesh straße Nr.155'), (''),
  ('Deutschland Berlin Charlotenburg Pudbulesky Alleee 52'),
  ('United State of America Washington DC. Farbod Alle'), ('');

DECLARE @RowsToInsert INT = 10000;

;WITH rowcounts AS
(
  SELECT (SELECT COUNT(*) FROM @TelNumber) AS [TelNumberRows],
         (SELECT COUNT(*) FROM @FirstName) AS [FirstNameRows],
         (SELECT COUNT(*) FROM @LastName) AS [LastNameRows],
         (SELECT COUNT(*) FROM @Address) AS [AddressRows]
), nums AS
(
  SELECT TOP (@RowsToInsert)
         (CRYPT_GEN_RANDOM(1) % rc.TelNumberRows) + 1 AS [RandomTelNumberID],
         (CRYPT_GEN_RANDOM(1) % rc.FirstNameRows) + 1 AS [RandomFirstNameID],
         (CRYPT_GEN_RANDOM(1) % rc.LastNameRows) + 1 AS [RandomLastNameID],
         (CRYPT_GEN_RANDOM(1) % rc.AddressRows) + 1 AS [RandomAddressID]
  FROM   rowcounts rc
  CROSS JOIN msdb.sys.all_columns sac1
  CROSS JOIN msdb.sys.all_columns sac2
)
-- INSERT dbo.Unsprstb(Firstname, Lastname, Tel, Address)
SELECT fn.Name, ln.Name, tn.Num, ad.Addr
FROM   @FirstName fn
FULL JOIN nums
        ON nums.RandomFirstNameID = fn.FirstNameID
FULL JOIN @LastName ln
        ON ln.LastNameID = nums.RandomLastNameID
FULL JOIN @TelNumber tn
        ON tn.TelNumberID = nums.RandomTelNumberID
FULL JOIN @Address ad
        ON ad.AddressID = nums.RandomAddressID;

Remarques :

  • La FULL JOIN s sont nécessaires au lieu de INNER JOIN s pour obtenir le @RowsToInsert entier nombre de lignes.
  • Les lignes en double sont possibles en raison de la nature même de cette randomisation ET du fait de ne pas les filtrer en utilisant DISTINCT . Cependant, DISTINCT ne peut pas être utilisé avec les exemples de données donnés dans la question car le nombre d'éléments dans chaque variable de tableau/table ne fournit que 6 300 combinaisons uniques et le nombre de lignes à générer est de 10 000. Si d'autres valeurs sont ajoutées aux variables du tableau de sorte que le nombre total de combinaisons uniques possibles dépasse le nombre de lignes demandé, alors soit le DISTINCT le mot-clé peut être ajouté aux nums CTE, ou la requête peut être restructurée pour simplement CROSS JOIN toute la variable de table, inclure un ROW_COUNT() champ, et saisissez le TOP(n) en utilisant ORDER BY NEWID() .
  • Le INSERT est commenté afin qu'il soit plus facile de voir que la requête ci-dessus produit le résultat souhaité. Décommentez simplement le INSERT pour que la requête effectue l'opération DML réelle.