В този код възникват няколко проблема, които трябва да бъдат решени:
-
По отношение на посочения въпрос, когато получите System.Security.SecurityException грешка, която се отнася до кода, който се опитва да достигне извън базата данни, нещо, което не е разрешено в
SAFE
монтаж. Как ще поправите това зависи от това, което се опитвате да постигнете.- Ако се опитвате да осъществите достъп до файловата система, да прочетете от системния регистър, да получите променлива на средата, да получите достъп до мрежата за връзка, различна от SQL Server (напр. http, ftp) и т.н., тогава асемблирането се нуждае от
PERMISSION_SET
отEXTERNAL_ACCESS
. За да настроите сборката си на нещо различно отSAFE
, трябва да:- Създайте сертификат или асиметричен ключ въз основа на същия ключ, който сте използвали за подписване на вашата сборка (т.е. дайте му силно име), създайте вход въз основа на този сертификат или асиметричен ключ и след това предоставете
EXTERNAL ACCESS ASSEMBLY
разрешение за това влизане. Този метод е чудесен предпочитан пред другия метод, който е: - Настройте базата данни, съдържаща сборката, на
TRUSTWORTHY ON
. Този метод трябва да се използва само в краен случай, ако не е възможно да се подпише сборката. Или за целите на бързото тестване. Задаване на база данни наTRUSTWORTHY ON
отваря екземпляра ви за потенциални заплахи за сигурността и трябва да се избягва, дори ако е по-бърз/лесен от другия метод.
- Създайте сертификат или асиметричен ключ въз основа на същия ключ, който сте използвали за подписване на вашата сборка (т.е. дайте му силно име), създайте вход въз основа на този сертификат или асиметричен ключ и след това предоставете
-
Ако се опитвате да получите достъп до екземпляра на SQL Server, в който вече сте влезли, тогава имате опцията да използвате връзката в процеса на
Context Connection = true;
което може да се направи вSAFE
монтаж. Това предложи @Marc в своя отговор. Въпреки че определено има предимства от използването на този тип връзка и докато контекстната връзка беше подходящият избор в този конкретен сценарий, е прекалено опростено и неправилно да се твърди, че трябва винаги използвайте този тип връзка. Нека да разгледаме положителните и отрицателните аспекти на контекстната връзка :- Положителни:
- Може да се направи в
SAFE
монтаж. - Много ниско, ако има такова, натоварване на връзката, тъй като това не е допълнителна връзка.
- Е част от текущата сесия, така че всеки SQL, който изпълнявате, има достъп до елементи, базирани на сесия, като локални временни таблици и
CONTEXT_INFO
.
- Може да се направи в
-
Негативи:
- Не може да се използва, ако имитацията е активирана.
- Може да се свързва само с текущия екземпляр на SQL Server.
- Когато се използва във функции (скаларни и с таблични стойности), той има всички същите ограничения, които имат T-SQL функциите (напр. не са разрешени операции със страничен ефект), освен че можете да изпълнявате съхранени процедури само за четене.
- На функциите с таблични стойности не е разрешено да предават своите резултати обратно, ако прочетат набор от резултати.
Всички тези „негативи“ са разрешени при използване на обикновена/външна връзка, дори ако е към същия екземпляр, от който изпълнявате този код.
- Положителни:
- Ако се опитвате да осъществите достъп до файловата система, да прочетете от системния регистър, да получите променлива на средата, да получите достъп до мрежата за връзка, различна от SQL Server (напр. http, ftp) и т.н., тогава асемблирането се нуждае от
-
Ако се свързвате към екземпляра, от който изпълнявате този код, и използвате външна/обикновена връзка, тогава няма нужда да посочвате името на сървъра или дори да използвате
localhost
. Предпочитаният синтаксис еServer = (local)
който използва споделена памет, докато другите може понякога да използват TCP/IP, което не е толкова ефективно. -
Освен ако нямате много конкретна причина да го направите, не използвайте
Persist Security Info=True;
-
Добра практика е
Dispose()
на вашияSqlCommand
-
По-ефективно е да извикате
insertcommand.Parameters.Add()
точно предиfor
цикъл и след това вътре в цикъла просто задайте стойността чрезfirstname.Value =
, което вече правите, така че просто преместетеinsertcommand.Parameters.Add()
редове точно предиfor
ред. -
tel
/@tel
/listtelnumber
саINT
вместоVARCHAR
/string
. Телефонните номера, също като пощенските кодове и социалноосигурителните номера (SSN), не числа, дори ако изглеждат такива.INT
не може да съхранява водещ0
s или нещо катоex.
за означаване на „разширение“. -
Като се има предвид всичко това, дори всичко по-горе да бъде коригирано, все още има огромен проблем с този код, който трябва да се реши :това е доста опростена операция за изпълнение в прав T-SQL и правенето на това в SQLCLR е прекалено сложно, по-трудно и по-скъпо за поддръжка и много по-бавно. Този код изпълнява 10 000 отделни транзакции, докато може толкова лесно да се направи като една заявка, базирана на набор (т.е. една транзакция). Можете да опаковате своя
for
цикъл в транзакция, което би я ускорило, но все пак винаги ще бъде по-бавно от базирания на набор T-SQL подход, тъй като все още трябва да издаде 10 000 отделниINSERT
изявления. Можете лесно да рандомизирате в T-SQL, като използватеNEWID()
или CRYPT_GEN_RANDOM който беше въведен в SQL Server 2008. (моля, вижте АКТУАЛИЗАЦИЯ раздел по-долу)
Ако искате да научите повече за SQLCLR, моля, разгледайте поредицата, която пиша за SQL Server Central: Стълбище към SQLCLR (изисква се безплатна регистрация).
АКТУАЛИЗАЦИЯ
Ето един чист T-SQL метод за генериране на тези произволни данни, като се използват стойностите от въпроса. Лесно е да добавите нови стойности към която и да е от 4-те променливи на таблицата (за да увеличите броя на възможните комбинации), тъй като заявката динамично настройва диапазона на рандомизиране, за да пасне на каквито и да е данни във всяка променлива на таблицата (т.е. редове 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;
Бележки:
FULL JOIN
s са необходими вместоINNER JOIN
s, за да получите целия@RowsToInsert
количество редове.- Възможни са дублиращи се редове поради самото естество на тази рандомизация И не филтрирането им с помощта на
DISTINCT
. Въпреки това,DISTINCT
не може да се използва с дадените примерни данни във въпроса, тъй като броят на елементите във всяка променлива на масив / таблица осигурява само 6300 уникални комбинации, а заявеният брой редове за генериране е 10 000. Ако към променливите на таблицата се добавят повече стойности, така че общият брой възможни уникални комбинации да се повиши над заявения брой редове, тогава илиDISTINCT
ключова дума може да се добави къмnums
CTE или заявката може да бъде преструктурирана просто доCROSS JOIN
цялата променлива на таблицата включваROW_COUNT()
и вземетеTOP(n)
използвайкиORDER BY NEWID()
. INSERT
е коментиран, така че е по-лесно да се види, че заявката по-горе дава желания резултат. Просто премахнете коментара отINSERT
за да накарате заявката да извърши действителната DML операция.