W tym kodzie pojawia się kilka problemów, które należy rozwiązać:
-
Jeśli chodzi o zadane pytanie, gdy pojawi się System.Security.SecurityException błąd, który odnosi się do kodu próbującego sięgnąć poza bazę danych, coś, co nie jest dozwolone w
BEZPIECZNY
montaż. Sposób, w jaki to naprawisz, zależy od tego, co próbujesz osiągnąć.- Jeśli próbujesz uzyskać dostęp do systemu plików, odczytać z rejestru, uzyskać zmienną środowiskową, uzyskać dostęp do sieci dla połączenia innego niż SQL Server (np. http, ftp) itp., wtedy zespół potrzebuje
PERMISSION_SET z EXTERNAL_ACCESS
. Aby ustawić swój zespół na cokolwiek innego niżBEZPIECZNY
, musisz:- Utwórz certyfikat lub klucz asymetryczny na podstawie tego samego klucza, którego użyłeś do podpisania swojego zestawu (tj. nadaj mu silną nazwę), utwórz login oparty na tym certyfikacie lub kluczu asymetrycznym, a następnie przyznaj
DOSTĘP ZEWNĘTRZNY MONTAŻ
uprawnienia do tego logowania. Ta metoda jest wspaniale preferowana w stosunku do innej metody, którą jest: - Ustaw bazę danych zawierającą zestaw na
TRUSTWORTHY ON
. Ta metoda powinna być stosowana tylko w ostateczności, jeśli nie ma możliwości podpisania zgromadzenia. Lub do szybkiego testowania. Ustawianie bazy danych naZAUFANIE NA
otwiera Twoją instancję na potencjalne zagrożenia bezpieczeństwa i należy tego unikać, nawet jeśli jest szybsza / łatwiejsza niż druga metoda.
- Utwórz certyfikat lub klucz asymetryczny na podstawie tego samego klucza, którego użyłeś do podpisania swojego zestawu (tj. nadaj mu silną nazwę), utwórz login oparty na tym certyfikacie lub kluczu asymetrycznym, a następnie przyznaj
-
Jeśli próbujesz uzyskać dostęp do instancji SQL Server, do której jesteś już zalogowany, możesz użyć połączenia w procesie
Context Connection =true;
co można zrobić wSAFE
montaż. To właśnie zasugerował @Marc w swojej odpowiedzi. Chociaż korzystanie z tego typu połączenia zdecydowanie przynosi korzyści i chociaż połączenie kontekstowe było właściwym wyborem w tym konkretnym scenariuszu, stwierdzenie, że należy zawsze jest zbyt uproszczone i nieprawidłowe użyj tego typu połączenia. Przyjrzyjmy się pozytywnym i negatywnym aspektom Powiązania kontekstu :- Pozytywy:
- Można to zrobić w
BEZPIECZNYM
montaż. - Bardzo niski, jeśli w ogóle, koszt połączenia, ponieważ nie jest to dodatkowe połączenie.
- Jest częścią bieżącej sesji, więc każdy wykonywany kod SQL ma dostęp do elementów sesji, takich jak lokalne tabele tymczasowe i
CONTEXT_INFO
.
- Można to zrobić w
-
Negatywy:
- Nie można użyć, jeśli włączona jest personifikacja.
- Może łączyć się tylko z bieżącą instancją SQL Server.
- Gdy jest używany w funkcjach (skalarnych i z wartościami tabelarycznymi), ma wszystkie te same ograniczenia, które mają funkcje T-SQL (np. nie są dozwolone żadne operacje uboczne), z wyjątkiem tego, że można wykonywać procedury przechowywane tylko do odczytu.
- Funkcje o wartościach tabelarycznych nie mogą przesyłać strumieniowo swoich wyników z powrotem, jeśli odczytują zestaw wyników.
Wszystkie te „negatywy” są dozwolone podczas korzystania ze zwykłego/zewnętrznego połączenia, nawet jeśli jest to ta sama instancja, z której wykonujesz ten kod.
- Pozytywy:
- Jeśli próbujesz uzyskać dostęp do systemu plików, odczytać z rejestru, uzyskać zmienną środowiskową, uzyskać dostęp do sieci dla połączenia innego niż SQL Server (np. http, ftp) itp., wtedy zespół potrzebuje
-
Jeśli łączysz się z instancją, z której wykonujesz ten kod i używasz połączenia zewnętrznego / zwykłego, nie musisz określać nazwy serwera ani nawet używać
localhost
. Preferowana składnia toServer =(local)
który używa pamięci współdzielonej, podczas gdy inne mogą czasami używać protokołu TCP/IP, który nie jest tak wydajny. -
Jeśli nie masz bardzo konkretnego powodu, nie używaj
Persist Security Info=True;
-
Dobrą praktyką jest
Dispose()
TwojegoSqlCommand
-
Bardziej wydajne jest wywołanie
insertcommand.Parameters.Add()
tuż przedfor
pętli, a następnie wewnątrz pętli, po prostu ustaw wartość za pomocąfirstname.Value =
, co już robisz, więc po prostu przenieśinsertcommand.Parameters.Add()
wiersze tuż przedfor
linia. -
tel
/@tel
/numer listy
sąINT
zamiastVARCHAR
/ciąg
. Numery telefonów, podobnie jak kody pocztowe i numery ubezpieczenia społecznego (SSN), nie numery, nawet jeśli tak się wydaje.INT
nie można przechowywać wiodącego0
s lub coś takiego jaknp.
oznaczające „rozszerzenie”. -
Biorąc to wszystko pod uwagę, nawet jeśli wszystkie powyższe kwestie zostaną poprawione, nadal istnieje ogromny problem z tym kodem, którym należy się zająć :jest to dość uproszczona operacja do wykonania w prostym języku T-SQL, a wykonanie tego w SQLCLR jest nadmiernie skomplikowane, trudniejsze i bardziej kosztowne w utrzymaniu oraz znacznie wolniejsze. Ten kod wykonuje 10 000 oddzielnych transakcji, podczas gdy można go tak łatwo wykonać jako pojedyncze zapytanie oparte na zestawie (tj. Jedna transakcja). Możesz zawinąć swój
dla
pętla w transakcji, która by ją przyspieszyła, ale nadal będzie wolniejsza niż podejście T-SQL oparte na zbiorach, ponieważ nadal wymaga wydania 10 000 oddzielnychINSERT
sprawozdania. Możesz łatwo losować w T-SQL za pomocąNEWID()
lub CRYPT_GEN_RANDOM który został wprowadzony w SQL Server 2008. (zobacz AKTUALIZACJA sekcja poniżej)
Jeśli chcesz dowiedzieć się więcej o SQLCLR, zapoznaj się z serią, którą piszę dla SQL Server Central: Schody do SQLCLR (wymagana bezpłatna rejestracja).
AKTUALIZUJ
Oto czysta metoda T-SQL generowania tych losowych danych przy użyciu wartości z pytania. Łatwo jest dodać nowe wartości do dowolnej z 4 zmiennych tabeli (aby zwiększyć liczbę możliwych kombinacji), ponieważ zapytanie dynamicznie dostosowuje zakres randomizacji, aby dopasować dowolne dane w każdej zmiennej tabeli (tj. wiersze 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;
Uwagi:
PEŁNE DOŁĄCZENIE
s są potrzebne zamiastINNER JOIN
s, aby uzyskać cały@RowsToInsert
liczba wierszy.- Zduplikowane wiersze są możliwe ze względu na samą naturę tej randomizacji ORAZ nie odfiltrowywanie ich za pomocą
DISTINCT
. JednakDISTINCT
nie może być użyty z podanymi przykładowymi danymi w pytaniu, ponieważ liczba elementów w każdej zmiennej tablicy / tabeli zapewnia tylko 6300 unikalnych kombinacji, a żądana liczba wierszy do wygenerowania to 10 000. Jeśli do zmiennych tabeli zostanie dodanych więcej wartości, tak że łączna liczba możliwych unikalnych kombinacji wzrośnie powyżej żądanej liczby wierszy, wtedy alboDISTINCT
słowo kluczowe można dodać donums
CTE lub zapytanie może zostać zrestrukturyzowane, aby po prostuCROSS JOIN
wszystkie zmienne tabeli, zawierająROW_COUNT()
i chwyćTOP(n)
używającORDER BY NEWID()
. - Karta
WSTAW
jest wykomentowany, więc łatwiej jest zauważyć, że powyższe zapytanie daje pożądany wynik. Po prostu odkomentujINSERT
aby zapytanie wykonało rzeczywistą operację DML.