Il y a plusieurs problèmes dans ce code qui doivent être résolus :
-
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
deEXTERNAL_ACCESS
. Afin de définir votre assemblage sur autre chose queSAFE
, 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 surTRUSTWORTHY 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.
- 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
-
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 unSAFE
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
.
- Peut être fait dans un
-
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.
- Points positifs :
- 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
-
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 estServer = (local)
qui utilise la mémoire partagée alors que les autres utilisent parfois TCP/IP qui n'est pas aussi efficace. -
À moins que vous n'ayez une raison très précise de le faire, n'utilisez pas
Persist Security Info=True;
-
C'est une bonne pratique de
Dispose()
de votreSqlCommand
-
Il est plus efficace d'appeler le
insertcommand.Parameters.Add()
juste avant lefor
boucle, puis à l'intérieur de la boucle, définissez simplement la valeur viafirstname.Value =
, ce que vous faites déjà, il suffit donc de déplacer leinsertcommand.Parameters.Add()
lignes juste avant lefor
ligne. -
tel
/@tel
/listtelnumber
sontINT
au lieu deVARCHAR
/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 le0
de début s ou quelque chose commeex.
pour signifier une "extension". -
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 000INSERT
distincts déclarations. Vous pouvez facilement randomiser dans T-SQL en utilisant soitNEWID()
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 deINNER 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 leDISTINCT
le mot-clé peut être ajouté auxnums
CTE, ou la requête peut être restructurée pour simplementCROSS JOIN
toute la variable de table, inclure unROW_COUNT()
champ, et saisissez leTOP(n)
en utilisantORDER 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 leINSERT
pour que la requête effectue l'opération DML réelle.