Der er flere problemer i gang i denne kode, som skal løses:
-
Med hensyn til det angivne spørgsmål, når du får en System.Security.SecurityException fejl, der henviser til koden, der forsøger at nå uden for databasen, noget der ikke er tilladt i en
SAFE
montage. Hvordan du løser dette, afhænger af, hvad du forsøger at opnå.- Hvis du forsøger at få adgang til filsystemet, læse fra registreringsdatabasen, hente en miljøvariabel, få adgang til netværket for en ikke-SQL-serverforbindelse (f.eks. http, ftp) osv., så skal samlingen have en
PERMISSION_SET
afEXTERNAL_ACCESS
. For at indstille din samling til noget andet endSAFE
, skal du enten:- Opret et certifikat eller en asymmetrisk nøgle baseret på den samme nøgle, som du brugte til at underskrive din samling (dvs. giv den et stærkt navn), opret et login baseret på det pågældende certifikat eller den asymmetriske nøgle, og giv derefter
EKSTERN ADGANG MONTERING tilladelse til det pågældende login. Denne metode er meget foretrækkes frem for den anden metode, som er: - Indstil databasen, der indeholder samlingen til
TRUSTWORTHY ON
. Denne metode bør kun bruges som en sidste udvej, hvis det ikke er muligt at underskrive forsamlingen. Eller til hurtige testformål. Indstilling af en database tilTRUSTWORTHY ON
åbner din instans op for potentielle sikkerhedstrusler og bør undgås, selvom det er hurtigere/lettere end den anden metode.
- Opret et certifikat eller en asymmetrisk nøgle baseret på den samme nøgle, som du brugte til at underskrive din samling (dvs. giv den et stærkt navn), opret et login baseret på det pågældende certifikat eller den asymmetriske nøgle, og giv derefter
-
Hvis du forsøger at få adgang til den SQL Server-instans, som du allerede er logget på, så har du mulighed for at bruge den igangværende forbindelse af
Context Connection =true;
hvilket kan gøres i enSAFE
montage. Dette er, hvad @Marc foreslog i sit svar. Selvom der helt sikkert er fordele ved at bruge denne type forbindelse, og selvom kontekstforbindelsen var det passende valg i dette særlige scenarie, er det alt for forsimplet og forkert at sige, at du altid skal bruge denne type forbindelse. Lad os se på de positive og negative aspekter af Kontekstforbindelsen :- Positivt:
- Kan gøres i en
SAFE
samling. - Meget lave, hvis nogen, forbindelsesomkostninger, da det ikke er en ekstra forbindelse.
- Er en del af den aktuelle session, så enhver SQL, du udfører, har adgang til sessionsbaserede elementer såsom lokale midlertidige tabeller og
CONTEXT_INFO
.
- Kan gøres i en
-
Negativer:
- Kan ikke bruges, hvis personefterligning er blevet aktiveret.
- Kan kun oprette forbindelse til den aktuelle SQL Server-instans.
- Når den bruges i funktioner (skalær og tabelværdi), har den alle de samme begrænsninger, som T-SQL-funktioner har (f.eks. er der ingen bivirkninger tilladt), bortset fra at du kan udføre skrivebeskyttede lagrede procedurer.
- Tabelværdierede funktioner har ikke tilladelse til at streame deres resultater tilbage, hvis de læser et resultatsæt.
Alle disse "negative" er tilladt, når du bruger en almindelig/ekstern forbindelse, selvom det er til den samme instans, du udfører denne kode fra.
- Positivt:
- Hvis du forsøger at få adgang til filsystemet, læse fra registreringsdatabasen, hente en miljøvariabel, få adgang til netværket for en ikke-SQL-serverforbindelse (f.eks. http, ftp) osv., så skal samlingen have en
-
Hvis du opretter forbindelse til den instans, som du udfører denne kode fra og bruger en ekstern / almindelig forbindelse, behøver du ikke angive servernavnet eller endda bruge
localhost
. Den foretrukne syntaks erServer =(lokal)
som bruger delt hukommelse, mens de andre nogle gange bruger TCP/IP, hvilket ikke er så effektivt. -
Medmindre du har en meget specifik grund til at gøre det, skal du ikke bruge
Persist Security Info=True;
-
Det er en god praksis at
Dispose()
af dinSqlCommand
-
Det er mere effektivt at kalde
insertcommand.Parameters.Add()
lige førfor
løkke, og derefter inde i løkken, skal du blot indstille værdien viafirstname.Value =
, hvilket du allerede gør, så flyt bareinsertcommand.Parameters.Add()
linjer til lige førfor
linje. -
tlf
/@tel
/listenummer
erINT
i stedet forVARCHAR
/streng
. Telefonnumre, ligesom postnumre og socialsikringsnumre (SSN'er), er ikke tal, selvom de ser ud til at være det.INT
kan ikke gemme førende0
s eller noget somex.
for at betegne en "udvidelse". -
Når alt dette er sagt, selvom alt ovenstående er rettet, er der stadig et stort problem med denne kode, som bør løses :dette er en ret forenklet operation at udføre i lige T-SQL, og at gøre dette i SQLCLR er overkompliceret, sværere og dyrere at vedligeholde og meget langsommere. Denne kode udfører 10.000 separate transaktioner, hvorimod den så let kunne udføres som en enkelt sæt-baseret forespørgsel (dvs. én transaktion). Du kan pakke din
til
sløjfe i en transaktion, som ville fremskynde den, men den vil stadig altid være langsommere end den sæt-baserede T-SQL-tilgang, da den stadig skal udstede 10.000 separateINSERT
udsagn. Du kan nemt randomisere i T-SQL ved at bruge entenNEWID()
eller CRYPT_GEN_RANDOM som blev introduceret i SQL Server 2008. (se venligst OPDATERING afsnit nedenfor)
Hvis du vil lære mere om SQLCLR, så tjek venligst serien, jeg skriver til SQL Server Central: Trappe til SQLCLR (gratis registrering påkrævet).
OPDATERING
Her er en ren T-SQL-metode til at generere disse tilfældige data ved hjælp af værdierne fra spørgsmålet. Det er nemt at tilføje nye værdier til enhver af de 4 tabelvariabler (for at øge antallet af mulige kombinationer), da forespørgslen dynamisk justerer randomiseringsområdet, så det passer til de data, der er i hver tabelvariabel (dvs. række 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;
Bemærkninger:
FULD JOIN
s er nødvendige i stedet forINNER JOIN
s for at få hele@RowsToInsert
antal rækker.- Duplikerede rækker er mulige på grund af selve arten af denne randomisering OG ikke filtrering af dem ved at bruge
DISTINCT
. DogDISTINCT
kan ikke bruges med de givne eksempeldata i spørgsmålet, da antallet af elementer i hver array/tabelvariabel kun giver 6300 unikke kombinationer, og det ønskede antal rækker, der skal genereres, er 10.000. Hvis flere værdier tilføjes til tabelvariablerne, således at det samlede antal mulige unikke kombinationer stiger over det anmodede antal rækker, skal entenDISTINCT
nøgleord kan tilføjes tilnums
CTE, eller forespørgslen kan omstruktureres til blot atCROSS JOIN
alle tabelvariabler, inkludere enROW_COUNT()
feltet, og tag fat iTOP(n)
ved hjælp afORDER BY NEWID()
. INSERT er kommenteret ud, så det er nemmere at se, at forespørgslen ovenfor giver det ønskede resultat. Bare fjern kommentaren til INSERT
for at få forespørgslen til at udføre den faktiske DML-operation.