Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Msg 6522, avviso di livello 16 durante l'esecuzione della stored procedure clr

Ci sono diversi problemi in corso in questo codice che devono essere risolti:

  1. Per quanto riguarda la domanda indicata, quando ottieni un System.Security.SecurityException errore, che si riferisce al codice che tenta di raggiungere l'esterno del database, cosa che non è consentita in un SAFE assemblaggio. Il modo in cui lo risolvi dipende da ciò che stai cercando di ottenere.

    • Se stai tentando di accedere al file system, leggere dal registro, ottenere una variabile di ambiente, accedere alla rete per una connessione non SQL Server (ad es. http, ftp), ecc., l'assembly ha bisogno di un PERMISSION_SET di EXTERNAL_ACCESS . Per impostare il tuo assieme su qualcosa di diverso da SAFE , devi:
      • Crea un certificato o una chiave asimmetrica in base alla stessa chiave che hai utilizzato per firmare il tuo assembly (ovvero assegnagli un nome sicuro), crea un accesso basato su quel certificato o chiave asimmetrica, quindi concedi il EXTERNAL ACCESS ASSEMBLY autorizzazione a tale accesso. Questo metodo è ottimo preferito rispetto all'altro metodo, che è:
      • Imposta il database contenente l'assembly su TRUSTWORTHY ON . Questo metodo dovrebbe essere utilizzato solo come ultima risorsa se non è possibile firmare l'assemblea. O per scopi di test rapidi. Impostazione di un database su TRUSTWORTHY ON apre la tua istanza a potenziali minacce alla sicurezza e dovrebbe essere evitato, anche se più veloce/semplice rispetto all'altro metodo.
    • Se stai tentando di accedere all'istanza di SQL Server a cui hai già effettuato l'accesso, hai la possibilità di utilizzare la connessione in-process di Context Connection = true; che può essere fatto in un SAFE assemblaggio. Questo è ciò che @Marc ha suggerito nella sua risposta. Sebbene ci siano sicuramente dei vantaggi nell'usare questo tipo di connessione e mentre la connessione al contesto è stata la scelta appropriata in questo particolare scenario, è eccessivamente semplicistico e scorretto affermare che dovresti sempre utilizzare questo tipo di connessione. Diamo un'occhiata agli aspetti positivi e negativi della Connessione al contesto :

      • Positivi:
        • Può essere fatto in un SAFE montaggio.
        • Spessore di connessione molto basso, se presente, in quanto non è una connessione aggiuntiva.
        • Fa parte della sessione corrente, quindi qualsiasi SQL eseguito ha accesso a elementi basati sulla sessione come tabelle temporanee locali e CONTEXT_INFO .
      • Negativi:

        • Non può essere utilizzato se è stata abilitata la rappresentazione.
        • Può connettersi solo all'istanza corrente di SQL Server.
        • Quando viene utilizzato in Funzioni (scalare e con valori di tabella), ha tutte le stesse restrizioni delle funzioni T-SQL (ad es. non sono consentite operazioni con effetti collaterali), tranne per il fatto che puoi eseguire procedure memorizzate di sola lettura.
        • Le funzioni con valori di tabella non sono autorizzate a trasmettere i risultati in streaming se leggono un set di risultati.

        Tutti questi "negativi" sono consentiti quando si utilizza una connessione normale/esterna, anche se si tratta della stessa istanza da cui si esegue questo codice.

  2. Se ti stai connettendo all'istanza da cui stai eseguendo questo codice e utilizzando una connessione esterna/normale, non è necessario specificare il nome del server o utilizzare localhost . La sintassi preferita è Server = (local) che utilizza la memoria condivisa mentre gli altri a volte potrebbero utilizzare TCP/IP che non è così efficiente.

  3. A meno che tu non abbia un motivo molto specifico per farlo, non utilizzare Persist Security Info=True;

  4. È buona norma Dispose() del tuo SqlCommand

  5. È più efficiente chiamare insertcommand.Parameters.Add() appena prima del for loop, quindi all'interno del loop, imposta semplicemente il valore tramite firstname.Value = , cosa che stai già facendo, quindi sposta semplicemente il insertcommand.Parameters.Add() righe appena prima di for linea.

  6. tel / @tel / listtelnumber sono INT invece di VARCHAR / string . I numeri di telefono, proprio come i codici postali ei numeri di previdenza sociale (SSN), non numeri, anche se sembrano esserlo. INT impossibile memorizzare 0 iniziale s o qualcosa come ex. per indicare una "estensione".

  7. Detto questo, anche se tutto quanto sopra è corretto, c'è ancora un grosso problema con questo codice che dovrebbe essere risolto :questa è un'operazione piuttosto semplicistica da eseguire in T-SQL diretto, e farlo in SQLCLR è eccessivamente complicato, più difficile e più costoso da mantenere e molto più lento. Questo codice sta eseguendo 10.000 transazioni separate mentre potrebbe essere facilmente eseguito come una singola query basata su set (cioè una transazione). Potresti avvolgere il tuo for loop in una transazione che la accelererebbe, ma sarà comunque sempre più lenta dell'approccio T-SQL basato su set poiché deve ancora emettere 10.000 INSERT separati dichiarazioni. Puoi facilmente randomizzare in T-SQL usando NEWID() o CRYPT_GEN_RANDOM che è stato introdotto in SQL Server 2008. (consultare UPDATE sezione seguente)

Se vuoi saperne di più su SQLCLR, dai un'occhiata alla serie che sto scrivendo per SQL Server Central: Scala per SQLCLR (richiesta registrazione gratuita).

AGGIORNAMENTO

Ecco un metodo T-SQL puro per generare questi dati casuali, utilizzando i valori della domanda. È facile aggiungere nuovi valori a una qualsiasi delle 4 variabili della tabella (per aumentare il numero di possibili combinazioni) poiché la query regola dinamicamente l'intervallo di randomizzazione per adattarsi a qualsiasi dato si trovi in ​​ciascuna variabile della tabella (es. righe 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;

Note:

  • Il FULL JOIN s sono necessari invece di INNER JOIN s per ottenere l'intero @RowsToInsert numero di righe.
  • Sono possibili righe duplicate a causa della natura stessa di questa randomizzazione E del fatto di non filtrarle utilizzando DISTINCT . Tuttavia, DISTINCT non può essere utilizzato con i dati di esempio forniti nella domanda poiché il numero di elementi in ciascuna variabile di matrice/tabella fornisce solo 6300 combinazioni univoche e il numero richiesto di righe da generare è 10.000. Se vengono aggiunti più valori alle variabili della tabella in modo tale che il totale delle possibili combinazioni univoche superi il numero di righe richiesto, allora il DISTINCT la parola chiave può essere aggiunta ai nums CTE, oppure la query può essere ristrutturata semplicemente in CROSS JOIN tutte le variabili della tabella, includono un ROW_COUNT() campo e prendi il TOP(n) utilizzando ORDER BY NEWID() .
  • Il INSERT è commentato, quindi è più facile vedere che la query sopra produce il risultato desiderato. Decommenta semplicemente il INSERT per fare in modo che la query esegua l'effettiva operazione DML.