Msg 6522,执行 clr 存储过程期间的 16 级警告
Msg 6522, Level 16 warning during execution of clr stored procedure
我想创建 SQL Server CLR 存储过程,用于在 SQL Server 2012 的 table 中插入一些行。
这是我的 C# 代码:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void InsertingRows ()
{
// Put your code here
Random rnd = new Random();
List<int> listtelnumber = new List<int>(new int[] { 1525407, 5423986, 1245398, 32657891, 123658974, 7896534, 12354698 });
List<string> listfirstname = new List<string>(new string[] { "Babak", "Carolin", "Martin", "Marie", "Susane", "Michail", "Ramona", "Ulf", "Dirk", "Sebastian" });
List<string> listlastname = new List<string>(new string[] { "Bastan", "Krause", "Rosner", "Gartenmeister", "Rentsch", "Benn", "Kycik", "Leuoth", "Kamkar", "Kolaee" });
List<string> listadres = new List<string>(new string[] { "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",
"" });
using (SqlConnection conn = new SqlConnection("Data Source=WIN2012SERVER02;Initial Catalog=test;Persist Security Info=True;User ID=di_test;Password=di_test"))
{
SqlCommand insertcommand = new SqlCommand();
SqlParameter firstname = new SqlParameter("@fname", SqlDbType.VarChar);
SqlParameter lastname = new SqlParameter("@lname", SqlDbType.VarChar);
SqlParameter tel = new SqlParameter("@tel", SqlDbType.Int);
SqlParameter adres = new SqlParameter("@adres", SqlDbType.NVarChar);
conn.Open();
for (int i = 0; i < 10000; i++)
{
int tn = rnd.Next(0, 6);
int fn = rnd.Next(0, 9);
int ln = rnd.Next(0, 9);
int an = rnd.Next(0, 9);
firstname.Value = listfirstname[fn];
lastname.Value = listlastname[ln];
tel.Value = listtelnumber[tn];
adres.Value = listadres[an];
insertcommand.Parameters.Add(firstname);
insertcommand.Parameters.Add(lastname);
insertcommand.Parameters.Add(tel);
insertcommand.Parameters.Add(adres);
insertcommand.CommandText = "INSERT dbo.Unsprstb(Firstname,Lastname,Tel,adress) VALUES(@fname,@lname,@tel,@adres)";
insertcommand.Connection = conn;
insertcommand.ExecuteNonQuery();
}
conn.Close();
}
}
}
我可以在 SQL 服务器中成功构建、部署和发布我的代码,但是如果我在 SQL 服务器中 运行 这个 CLR 存储过程,我会看到这条消息:
Msg 6522, Level 16, State 1, procedure InsertingRows,Line 0
A .NET Framework error occurred during execution of user defined routine or aggregate 'InsertingRows':
System.Security.SecurityException: Error request for the permission of type "System.Data.SqlClient.SqlClientPermission, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089".
System.Security.SecurityException:
bei System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
bei System.Security.PermissionSet.Demand()
bei System.Data.Common.DbConnectionOptions.DemandPermission()
bei System.Data.SqlClient.SqlConnectionFactory.PermissionDemand(DbConnection outerConnection)
bei System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions)<br>
bei System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource
1 retry)
bei System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
bei System.Data.SqlClient.SqlConnection.Open()
bei StoredProcedures.InsertingRows()
我该如何解决这个问题?
在您的 SQL CLR C# 代码中,您应该 而不是 建立与显式服务器、数据库名称和凭据的连接 - 相反,使用 "context"连接:
using (SqlConnection conn = new SqlConnection("context connection=true"))
{
// do your stuff here...
}
这段代码中有几个问题需要解决:
关于上述问题,当您收到 System.Security.SecurityException 错误时,该错误指的是试图到达数据库外部的代码,有些东西这在 SAFE
程序集中是不允许的。如何解决此问题取决于您要完成的任务。
- 如果您尝试访问文件系统,请从注册表中读取,获取环境变量,访问非SQL服务器连接的网络(例如http,ftp),等等,然后程序集需要
EXTERNAL_ACCESS
的 PERMISSION_SET
。为了将程序集设置为 SAFE
以外的任何值,您需要:
- 根据您用于签署程序集的相同密钥创建证书或非对称密钥(即给它一个强名称),根据该证书或非对称密钥创建登录,然后授予
EXTERNAL ACCESS ASSEMBLY
对该登录的权限。这种方法大大优于另一种方法,后者是:
- 将包含程序集的数据库设置为
TRUSTWORTHY ON
。只有在无法对程序集进行签名时,才应将此方法用作最后的手段。或者用于快速测试目的。将数据库设置为 TRUSTWORTHY ON
会使您的实例面临潜在的安全威胁,应该避免,即使比其他方法更快/更容易。
如果您尝试访问您已经登录的 SQL 服务器实例,那么您可以选择使用 Context Connection = true;
的进程内连接可以在 SAFE
程序集中完成。这是@Marc 在他的回答中建议的。虽然使用这种类型的连接肯定有好处,而且在这种特定情况下上下文连接是合适的选择,但说你应该总是使用是过于简单化和不正确的这种类型的连接。让我们看看 Context Connection:
的积极和消极方面
- 优点:
- 可以在
SAFE
程序集中完成。
- 非常低的连接开销,因为它不是额外的连接。
- 是当前会话的一部分,因此您执行的任何 SQL 都可以访问基于会话的项目,例如本地临时 tables 和
CONTEXT_INFO
.
否定:
- 如果已启用模拟,则无法使用。
- 只能连接到当前 SQL 服务器实例。
- 在函数(标量和 Table-Valued)中使用时,它具有与 T-SQL 函数相同的所有限制(例如,不允许进行副作用操作),除了您可以执行只读存储过程。
- Table-如果值函数读取结果集,则不允许将其结果流式传输回。
所有这些 "negatives" 在使用常规/外部连接时都是允许的,即使它是您从中执行此代码的同一实例。
如果您连接到从中执行此代码的实例并使用外部/常规连接,则无需指定服务器名称,甚至无需使用 localhost
。首选语法是 Server = (local)
,它使用共享内存,而其他语法有时可能使用效率不高的 TCP/IP。
除非您有非常具体的原因,否则不要使用 Persist Security Info=True;
Dispose()
的 SqlCommand
是个好习惯
在for
循环之前调用insertcommand.Parameters.Add()
效率更高,然后在循环内部,只需通过firstname.Value =
设置值,您已经在做,所以只需将 insertcommand.Parameters.Add()
行移动到 for
行之前。
tel
/ @tel
/ listtelnumber
是 INT
而不是 VARCHAR
/ string
。电话号码,就像邮政编码和社会安全号码 (SSN) 一样,是 而不是 号码,即使它们看起来是。 INT
无法存储前导 0
或类似 ex.
的东西来表示 "extension".
综上所述,即使以上所有内容都已更正,这段代码仍然存在一个巨大的问题需要解决:这个在直接 T-SQL 中执行是一个相当简单的操作,而在 SQLCLR 中执行此操作过于复杂,维护起来更难、成本更高,而且速度更慢。此代码执行 10,000 个单独的事务,而它可以很容易地作为一个基于集合的查询(即一个事务)来完成。您可以将 for
循环包装在一个可以加快速度的事务中,但它仍然总是比基于集合的 T-SQL 方法慢,因为它仍然需要发出 10,000 个单独的 INSERT
语句。您可以使用 SQL Server 2008 中引入的 NEWID()
或 CRYPT_GEN_RANDOM 轻松地在 T-SQL 中随机化。(请参阅 UPDATE 下面的部分)
如果您想了解有关 SQLCLR 的更多信息,请查看我为 SQL Server Central 撰写的系列文章:Stairway to SQLCLR(需要免费注册)。
更新
这是使用问题中的值生成此随机数据的纯 T-SQL 方法。很容易向 4 个 table 变量中的任何一个添加新值(以增加可能组合的数量),因为查询会动态调整随机化范围以适应每个 table 变量中的任何数据(即第 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
来获取整个 @RowsToInsert
行数。
- 由于这种随机化的本质并且没有使用
DISTINCT
过滤掉它们,因此可能存在重复行。但是,DISTINCT
不能与问题中给定的示例数据一起使用,因为每个数组/table 变量中的元素数量仅提供 6300 个唯一组合,并且要求生成的行数为 10,000。如果向 table 变量添加更多值,使得可能的唯一组合总数超过请求的行数,则可以将 DISTINCT
关键字添加到 nums
CTE,或者查询可以重组为简单地 CROSS JOIN
所有 table 变量,包括一个 ROW_COUNT()
字段,并使用 ORDER BY NEWID()
.[=130= 获取 TOP(n)
]
INSERT
已被注释掉,因此更容易看出上面的查询产生了所需的结果。只需取消注释 INSERT
即可让查询执行实际的 DML 操作。
我想创建 SQL Server CLR 存储过程,用于在 SQL Server 2012 的 table 中插入一些行。
这是我的 C# 代码:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void InsertingRows ()
{
// Put your code here
Random rnd = new Random();
List<int> listtelnumber = new List<int>(new int[] { 1525407, 5423986, 1245398, 32657891, 123658974, 7896534, 12354698 });
List<string> listfirstname = new List<string>(new string[] { "Babak", "Carolin", "Martin", "Marie", "Susane", "Michail", "Ramona", "Ulf", "Dirk", "Sebastian" });
List<string> listlastname = new List<string>(new string[] { "Bastan", "Krause", "Rosner", "Gartenmeister", "Rentsch", "Benn", "Kycik", "Leuoth", "Kamkar", "Kolaee" });
List<string> listadres = new List<string>(new string[] { "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",
"" });
using (SqlConnection conn = new SqlConnection("Data Source=WIN2012SERVER02;Initial Catalog=test;Persist Security Info=True;User ID=di_test;Password=di_test"))
{
SqlCommand insertcommand = new SqlCommand();
SqlParameter firstname = new SqlParameter("@fname", SqlDbType.VarChar);
SqlParameter lastname = new SqlParameter("@lname", SqlDbType.VarChar);
SqlParameter tel = new SqlParameter("@tel", SqlDbType.Int);
SqlParameter adres = new SqlParameter("@adres", SqlDbType.NVarChar);
conn.Open();
for (int i = 0; i < 10000; i++)
{
int tn = rnd.Next(0, 6);
int fn = rnd.Next(0, 9);
int ln = rnd.Next(0, 9);
int an = rnd.Next(0, 9);
firstname.Value = listfirstname[fn];
lastname.Value = listlastname[ln];
tel.Value = listtelnumber[tn];
adres.Value = listadres[an];
insertcommand.Parameters.Add(firstname);
insertcommand.Parameters.Add(lastname);
insertcommand.Parameters.Add(tel);
insertcommand.Parameters.Add(adres);
insertcommand.CommandText = "INSERT dbo.Unsprstb(Firstname,Lastname,Tel,adress) VALUES(@fname,@lname,@tel,@adres)";
insertcommand.Connection = conn;
insertcommand.ExecuteNonQuery();
}
conn.Close();
}
}
}
我可以在 SQL 服务器中成功构建、部署和发布我的代码,但是如果我在 SQL 服务器中 运行 这个 CLR 存储过程,我会看到这条消息:
Msg 6522, Level 16, State 1, procedure InsertingRows,Line 0
A .NET Framework error occurred during execution of user defined routine or aggregate 'InsertingRows':
System.Security.SecurityException: Error request for the permission of type "System.Data.SqlClient.SqlClientPermission, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089".
System.Security.SecurityException:
bei System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
bei System.Security.PermissionSet.Demand()
bei System.Data.Common.DbConnectionOptions.DemandPermission()
bei System.Data.SqlClient.SqlConnectionFactory.PermissionDemand(DbConnection outerConnection)
bei System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource1 retry, DbConnectionOptions userOptions)<br> bei System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource
1 retry)
bei System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
bei System.Data.SqlClient.SqlConnection.Open()
bei StoredProcedures.InsertingRows()
我该如何解决这个问题?
在您的 SQL CLR C# 代码中,您应该 而不是 建立与显式服务器、数据库名称和凭据的连接 - 相反,使用 "context"连接:
using (SqlConnection conn = new SqlConnection("context connection=true"))
{
// do your stuff here...
}
这段代码中有几个问题需要解决:
关于上述问题,当您收到 System.Security.SecurityException 错误时,该错误指的是试图到达数据库外部的代码,有些东西这在
SAFE
程序集中是不允许的。如何解决此问题取决于您要完成的任务。- 如果您尝试访问文件系统,请从注册表中读取,获取环境变量,访问非SQL服务器连接的网络(例如http,ftp),等等,然后程序集需要
EXTERNAL_ACCESS
的PERMISSION_SET
。为了将程序集设置为SAFE
以外的任何值,您需要:- 根据您用于签署程序集的相同密钥创建证书或非对称密钥(即给它一个强名称),根据该证书或非对称密钥创建登录,然后授予
EXTERNAL ACCESS ASSEMBLY
对该登录的权限。这种方法大大优于另一种方法,后者是: - 将包含程序集的数据库设置为
TRUSTWORTHY ON
。只有在无法对程序集进行签名时,才应将此方法用作最后的手段。或者用于快速测试目的。将数据库设置为TRUSTWORTHY ON
会使您的实例面临潜在的安全威胁,应该避免,即使比其他方法更快/更容易。
- 根据您用于签署程序集的相同密钥创建证书或非对称密钥(即给它一个强名称),根据该证书或非对称密钥创建登录,然后授予
如果您尝试访问您已经登录的 SQL 服务器实例,那么您可以选择使用
的积极和消极方面Context Connection = true;
的进程内连接可以在SAFE
程序集中完成。这是@Marc 在他的回答中建议的。虽然使用这种类型的连接肯定有好处,而且在这种特定情况下上下文连接是合适的选择,但说你应该总是使用是过于简单化和不正确的这种类型的连接。让我们看看 Context Connection:- 优点:
- 可以在
SAFE
程序集中完成。 - 非常低的连接开销,因为它不是额外的连接。
- 是当前会话的一部分,因此您执行的任何 SQL 都可以访问基于会话的项目,例如本地临时 tables 和
CONTEXT_INFO
.
- 可以在
否定:
- 如果已启用模拟,则无法使用。
- 只能连接到当前 SQL 服务器实例。
- 在函数(标量和 Table-Valued)中使用时,它具有与 T-SQL 函数相同的所有限制(例如,不允许进行副作用操作),除了您可以执行只读存储过程。
- Table-如果值函数读取结果集,则不允许将其结果流式传输回。
所有这些 "negatives" 在使用常规/外部连接时都是允许的,即使它是您从中执行此代码的同一实例。
- 优点:
- 如果您尝试访问文件系统,请从注册表中读取,获取环境变量,访问非SQL服务器连接的网络(例如http,ftp),等等,然后程序集需要
如果您连接到从中执行此代码的实例并使用外部/常规连接,则无需指定服务器名称,甚至无需使用
localhost
。首选语法是Server = (local)
,它使用共享内存,而其他语法有时可能使用效率不高的 TCP/IP。除非您有非常具体的原因,否则不要使用
Persist Security Info=True;
Dispose()
的SqlCommand
是个好习惯
在
for
循环之前调用insertcommand.Parameters.Add()
效率更高,然后在循环内部,只需通过firstname.Value =
设置值,您已经在做,所以只需将insertcommand.Parameters.Add()
行移动到for
行之前。tel
/@tel
/listtelnumber
是INT
而不是VARCHAR
/string
。电话号码,就像邮政编码和社会安全号码 (SSN) 一样,是 而不是 号码,即使它们看起来是。INT
无法存储前导0
或类似ex.
的东西来表示 "extension".综上所述,即使以上所有内容都已更正,这段代码仍然存在一个巨大的问题需要解决:这个在直接 T-SQL 中执行是一个相当简单的操作,而在 SQLCLR 中执行此操作过于复杂,维护起来更难、成本更高,而且速度更慢。此代码执行 10,000 个单独的事务,而它可以很容易地作为一个基于集合的查询(即一个事务)来完成。您可以将
for
循环包装在一个可以加快速度的事务中,但它仍然总是比基于集合的 T-SQL 方法慢,因为它仍然需要发出 10,000 个单独的INSERT
语句。您可以使用 SQL Server 2008 中引入的NEWID()
或 CRYPT_GEN_RANDOM 轻松地在 T-SQL 中随机化。(请参阅 UPDATE 下面的部分)
如果您想了解有关 SQLCLR 的更多信息,请查看我为 SQL Server Central 撰写的系列文章:Stairway to SQLCLR(需要免费注册)。
更新
这是使用问题中的值生成此随机数据的纯 T-SQL 方法。很容易向 4 个 table 变量中的任何一个添加新值(以增加可能组合的数量),因为查询会动态调整随机化范围以适应每个 table 变量中的任何数据(即第 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
来获取整个@RowsToInsert
行数。 - 由于这种随机化的本质并且没有使用
DISTINCT
过滤掉它们,因此可能存在重复行。但是,DISTINCT
不能与问题中给定的示例数据一起使用,因为每个数组/table 变量中的元素数量仅提供 6300 个唯一组合,并且要求生成的行数为 10,000。如果向 table 变量添加更多值,使得可能的唯一组合总数超过请求的行数,则可以将DISTINCT
关键字添加到nums
CTE,或者查询可以重组为简单地CROSS JOIN
所有 table 变量,包括一个ROW_COUNT()
字段,并使用ORDER BY NEWID()
.[=130= 获取TOP(n)
] INSERT
已被注释掉,因此更容易看出上面的查询产生了所需的结果。只需取消注释INSERT
即可让查询执行实际的 DML 操作。