Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Msg 6522, Warnung der Ebene 16 während der Ausführung der gespeicherten Prozedur clr

In diesem Code gibt es mehrere Probleme, die behoben werden müssen:

  1. In Bezug auf die angegebene Frage, wenn Sie eine System.Security.SecurityException erhalten Fehler, der sich auf den Code bezieht, der versucht, außerhalb der Datenbank zu gelangen, etwas, das in einem SAFE nicht erlaubt ist Montage. Wie Sie dies beheben, hängt davon ab, was Sie erreichen möchten.

    • Wenn Sie versuchen, auf das Dateisystem zuzugreifen, aus der Registrierung zu lesen, eine Umgebungsvariable abzurufen, auf das Netzwerk für eine Nicht-SQL-Server-Verbindung (z. B. http, ftp) zuzugreifen usw., dann benötigt die Assembly einen PERMISSION_SET von EXTERNAL_ACCESS . Um Ihre Assembly auf etwas anderes als SAFE einzustellen , müssen Sie entweder:
      • Erstellen Sie ein Zertifikat oder einen asymmetrischen Schlüssel basierend auf demselben Schlüssel, den Sie zum Signieren Ihrer Assembly verwendet haben (d. h. geben Sie ihr einen starken Namen), erstellen Sie eine Anmeldung basierend auf diesem Zertifikat oder asymmetrischen Schlüssel und gewähren Sie dann den EXTERNAL ACCESS ASSEMBLY Berechtigung zu diesem Login. Diese Methode ist großartig gegenüber der anderen Methode bevorzugt, nämlich:
      • Setzen Sie die Datenbank, die die Assembly enthält, auf TRUSTWORTHY ON . Diese Methode sollte immer nur als letztes Mittel verwendet werden, wenn es nicht möglich ist, die Versammlung zu signieren. Oder für schnelle Testzwecke. Setzen einer Datenbank auf TRUSTWORTHY ON öffnet Ihre Instanz potenziellen Sicherheitsbedrohungen und sollte vermieden werden, auch wenn es schneller/einfacher ist als die andere Methode.
    • Wenn Sie versuchen, auf die SQL Server-Instanz zuzugreifen, bei der Sie bereits angemeldet sind, haben Sie die Möglichkeit, die In-Process-Verbindung von Context Connection = true; zu verwenden was in einem SAFE erfolgen kann Montage. Dies hat @Marc in seiner Antwort vorgeschlagen. Obwohl die Verwendung dieser Art von Verbindung definitiv Vorteile hat und die Kontextverbindung in diesem speziellen Szenario die richtige Wahl war, ist es zu einfach und falsch zu sagen, dass Sie immer sollten Verwenden Sie diese Art der Verbindung. Sehen wir uns die positiven und negativen Aspekte der Kontextverbindung an :

      • Positive:
        • Kann in einem SAFE durchgeführt werden Montage.
        • Sehr geringer Verbindungsaufwand, wenn überhaupt, da es sich nicht um eine zusätzliche Verbindung handelt.
        • Ist Teil der aktuellen Sitzung, sodass jedes von Ihnen ausgeführte SQL Zugriff auf sitzungsbasierte Elemente wie lokale temporäre Tabellen und CONTEXT_INFO hat .
      • Negative:

        • Kann nicht verwendet werden, wenn Identitätswechsel aktiviert wurde.
        • Kann nur eine Verbindung zur aktuellen SQL Server-Instanz herstellen.
        • Wenn es in Funktionen (Skalar und Tabellenwert) verwendet wird, hat es dieselben Einschränkungen wie T-SQL-Funktionen (z. B. sind keine Operationen mit Nebeneffekten zulässig), außer dass Sie schreibgeschützte gespeicherte Prozeduren ausführen können.
        • Tabellenwertfunktionen dürfen ihre Ergebnisse nicht zurückstreamen, wenn sie eine Ergebnismenge lesen.

        Alle diese "Negativen" sind erlaubt, wenn eine normale/externe Verbindung verwendet wird, selbst wenn es sich um dieselbe Instanz handelt, von der aus Sie diesen Code ausführen.

  2. Wenn Sie eine Verbindung zu der Instanz herstellen, von der aus Sie diesen Code ausführen, und eine externe/normale Verbindung verwenden, müssen Sie den Servernamen nicht angeben oder sogar localhost verwenden . Die bevorzugte Syntax ist Server = (local) das Shared Memory verwendet, während die anderen manchmal TCP/IP verwenden, was nicht so effizient ist.

  3. Verwenden Sie Persist Security Info=True; nicht, es sei denn, Sie haben einen ganz bestimmten Grund dafür

  4. Es hat sich bewährt, Dispose() zu verwenden Ihres SqlCommand

  5. Es ist effizienter, insertcommand.Parameters.Add() aufzurufen direkt vor dem for Schleife und setzen Sie dann innerhalb der Schleife einfach den Wert über firstname.Value = , was Sie bereits tun, also verschieben Sie einfach insertcommand.Parameters.Add() Zeilen bis kurz vor for Linie.

  6. tel / @tel / listtelnumber sind INT statt VARCHAR / string . Telefonnummern, genau wie Postleitzahlen und Sozialversicherungsnummern (SSNs), sind nicht Nummern, auch wenn sie so aussehen. INT führendes 0 kann nicht gespeichert werden s oder so etwas wie ex. um eine "Erweiterung" anzuzeigen.

  7. All dies gesagt, selbst wenn alle oben genannten Punkte korrigiert werden, gibt es immer noch ein riesiges Problem mit diesem Code, das angegangen werden sollte :Dies ist eine ziemlich einfache Operation, die in direktem T-SQL ausgeführt werden kann, und dies in SQLCLR zu tun, ist zu kompliziert, schwieriger und kostspieliger zu warten und viel langsamer. Dieser Code führt 10.000 separate Transaktionen durch, während er so einfach als eine einzige satzbasierte Abfrage (d. h. eine Transaktion) durchgeführt werden könnte. Sie könnten Ihren for umschließen Schleife in einer Transaktion, was sie beschleunigen würde, aber sie ist immer noch langsamer als der satzbasierte T-SQL-Ansatz, da sie immer noch 10.000 separate INSERT ausgeben muss Aussagen. Sie können in T-SQL einfach randomisieren, indem Sie entweder NEWID() verwenden oder CRYPT_GEN_RANDOM die in SQL Server 2008 eingeführt wurde. (siehe UPDATE Abschnitt unten)

Wenn Sie mehr über SQLCLR erfahren möchten, sehen Sie sich bitte die Serie an, die ich für SQL Server Central schreibe: Treppe zu SQLCLR (kostenlose Registrierung erforderlich).

AKTUALISIEREN

Hier ist eine reine T-SQL-Methode zum Generieren dieser Zufallsdaten unter Verwendung der Werte aus der Frage. Es ist einfach, neue Werte zu jeder der 4 Tabellenvariablen hinzuzufügen (um die Anzahl möglicher Kombinationen zu erhöhen), da die Abfrage den Randomisierungsbereich dynamisch anpasst, damit er zu den Daten passt, die in jeder Tabellenvariablen enthalten sind (d. h. Zeilen 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;

Hinweise:

  • Der FULL JOIN s werden anstelle von INNER JOIN benötigt s, um den gesamten @RowsToInsert zu erhalten Anzahl der Zeilen.
  • Doppelte Zeilen sind aufgrund der Art dieser Randomisierung möglich UND werden nicht mit DISTINCT herausgefiltert . Allerdings DISTINCT kann nicht mit den angegebenen Beispieldaten in der Frage verwendet werden, da die Anzahl der Elemente in jeder Array- / Tabellenvariablen nur 6300 eindeutige Kombinationen vorsieht und die angeforderte Anzahl der zu generierenden Zeilen 10.000 beträgt. Wenn den Tabellenvariablen weitere Werte hinzugefügt werden, sodass die insgesamt möglichen eindeutigen Kombinationen über die angeforderte Anzahl von Zeilen hinausgehen, dann wird entweder DISTINCT Schlüsselwort kann zu den nums hinzugefügt werden CTE, oder die Abfrage kann einfach zu CROSS JOIN umstrukturiert werden alle Tabellenvariablen enthalten ein ROW_COUNT() Feld und schnappen Sie sich TOP(n) mit ORDER BY NEWID() .
  • Das INSERT ist auskommentiert, damit Sie leichter erkennen können, dass die obige Abfrage das gewünschte Ergebnis liefert. Kommentieren Sie einfach INSERT aus damit die Abfrage die eigentliche DML-Operation durchführt.