このコードには対処が必要な問題がいくつかあります:
<オール> <リ>
上記の質問に関して、System.Security.SecurityException が発生した場合 SAFE
では許可されていない、データベースの外部に到達しようとしているコードを指すエラー 組み立て。これを修正する方法は、何を達成しようとしているかによって異なります。
- ファイル システムへのアクセス、レジストリからの読み取り、環境変数の取得、非 SQL Server 接続 (http、ftp など) のネットワークへのアクセスなどを試みる場合、アセンブリには
PERMISSION_SET
EXTERNAL_ACCESS
の .アセンブリをSAFE
以外に設定するには 、次のいずれかを行う必要があります:- アセンブリの署名に使用したのと同じキーに基づいて証明書または非対称キーを作成し (つまり、厳密な名前を付けます)、その証明書または非対称キーに基づいてログインを作成し、
EXTERNAL ACCESS ASSEMBLY
そのログインへの許可。この方法は大いに 他の方法よりも優先されます: - アセンブリを含むデータベースを
TRUSTWORTHY ON
に設定します .このメソッドは、アセンブリに署名できない場合の最後の手段としてのみ使用してください。または、簡単なテスト目的で。データベースをTRUSTWORTHY ON
に設定する インスタンスを潜在的なセキュリティの脅威にさらす可能性があるため、他の方法よりも迅速/簡単であっても回避する必要があります。
<リ> - アセンブリの署名に使用したのと同じキーに基づいて証明書または非対称キーを作成し (つまり、厳密な名前を付けます)、その証明書または非対称キーに基づいてログインを作成し、
- 良い点:
SAFE
で実行可能 組み立て。- 追加の接続ではないため、接続オーバーヘッドがあったとしても非常に低いです。
- 現在のセッションの一部であるため、実行するすべての SQL は、ローカル一時テーブルや
CONTEXT_INFO
などのセッションベースのアイテムにアクセスできます .
<リ> - なりすましが有効になっている場合は使用できません。
- 現在の SQL Server インスタンスにのみ接続できます。
- 関数 (スカラーおよびテーブル値) で使用する場合、読み取り専用のストアド プロシージャを実行できることを除いて、T-SQL 関数と同じ制限がすべて適用されます (たとえば、副作用のある操作は許可されません)。
- テーブル値関数は、結果セットを読み取る場合、結果をストリーミングすることはできません。
既にログインしている SQL Server インスタンスにアクセスしようとしている場合は、Context Connection = true;
のインプロセス接続を使用するオプションがあります。 これは SAFE
で実行できます 組み立て。これは、@Marc が回答で提案したものです。このタイプの接続を使用することには確かに利点があり、この特定のシナリオではコンテキスト接続が適切な選択でしたが、常にすべきであると述べるのは過度に単純で間違っています。 このタイプの接続を使用します。 Context Connection のプラス面とマイナス面を見てみましょう :
ネガ:
通常/外部接続を使用する場合、このコードを実行しているのと同じインスタンスに対してであっても、これらの「ネガティブ」はすべて許可されます。
このコードを実行しているインスタンスに接続し、外部/通常の接続を使用している場合は、サーバー名を指定したり、 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 の個別のトランザクションを実行していますが、1 つのセットベースのクエリ (つまり、1 つのトランザクション) としては簡単に実行できます。 for
をラップできます トランザクションでループすると高速化されますが、10,000 個の個別の INSERT
を発行する必要があるため、セットベースの T-SQL アプローチよりも常に遅くなります。 ステートメント。 NEWID()
のいずれかを使用して、T-SQL で簡単にランダム化できます。 または CRYPT_GEN_RANDOM
これは SQL Server 2008 で導入されました (UPDATE を参照してください)。 以下のセクション)
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
INNER JOIN
の代わりに s が必要です@RowsToInsert
全体を取得する 行の量。- このランダム化の性質上、重複行が発生する可能性があり、かつ
DISTINCT
を使用して重複行を除外していない .ただし、DISTINCT
各配列/テーブル変数の要素数は 6300 の一意の組み合わせしか提供せず、生成する要求された行数は 10,000 であるため、問題の特定のサンプル データでは使用できません。テーブル変数にさらに値が追加され、可能な一意の組み合わせの合計が要求された行数を超える場合、DISTINCT
nums
にキーワードを追加できます CTE、またはクエリを単にCROSS JOIN
に再構築できます すべてのテーブル変数、ROW_COUNT()
を含めます フィールドを開き、TOP(n)
を取得しますORDER BY NEWID()
を使用 . INSERT
はコメントアウトされているため、上記のクエリが目的の結果を生成することを簡単に確認できます。INSERT
のコメントを外すだけです クエリに実際の DML 操作を実行させる