我正在尝试对结果集执行许多不同的查询,这些查询具有非常大的创建时间。为了获得性能提升,我希望使用临时表,并在此临时表上执行许多查询。
似乎很标准。然而,我正在努力在动态SQL中共享这个临时表。据我了解,每个SqlCommand对象都在自己的线程中执行,因此临时表位于不同的范围内 - 从而使查询线程无法访问它。
我尝试使用全局临时表,效果很好但不理想?
如何在动态SQL查询之间共享本地临时表?
我的意图:
using (var conn = new SqlClient.SqlConnection("..."))
{
// Creation involes many table joins in reality
String creationScript = "SELECT * FROM FooTable INTO #MyTemp";
SqlCommand createTempTbl = new SqlCommand(creationScript, conn);
createTempTbl.ExecuteNonQuery();
String query1 = "SELECT * FROM #MyTemp where id=@id";
SqlCommand query1Comm = new SqlCommand(query1, conn);
query1Comm.Parameters.Add("@id", ...);
String query2 = "SELECT * FROM #MyTemp where name=@name";
SqlCommand query2Comm = new SqlCommand(query2, conn);
query2Comm.Parameters.Add("@name", ...);
// And so on the queries go
} // Now want #MyTemp to be destroyed
答案 0 :(得分:4)
我知道这个问题已经发布了一段时间,但我相信答案非常简单。
我猜测您正在使用MS Enterprise Library访问数据库,这解释了为什么命令之间不存在临时表的原因。当命令完成时,Enterprise Library EXPLICITLY关闭与DB的连接(将其放回池中)。也就是说,除非您将命令放入事务中。 如果您直接使用ADO.NET(通过打开连接,构建和执行命令,然后关闭连接),您就不会遇到这个问题(当连接关闭时,这取决于您 - 这样更危险)。 以下是使用MS Enterprise Library和事务编写的一些代码(对不起,VB.NET):
' Get a reference to the database
Dim sqlNET As New Sql.SqlDatabase("*Your Connection String Here...*")
' Specify the transaction options
Dim oTranOpt As TransactionOptions = New TransactionOptions
' What type of isolation the transaction should have (V. Important):
oTranOpt.IsolationLevel = IsolationLevel.ReadUncommitted ' This one doesn't place locks on DB but allows dirty reads
' How long the transaction has to complete before implicitly failing (h,m,s):
oTranOpt.Timeout = New TimeSpan(0, 0, 30)
' Start the scope of the transation
Using oTranScope As TransactionScope = New TransactionScope(TransactionScopeOption.Required, oTranOpt)
' Create the connection to the DB. Not abs. necessary. EL will create one but best to do so.
Using Conn As Common.DbConnection = sqlNET.CreateConnection
' Create a Temp Table
sqlNET.ExecuteNonQuery(CommandType.Text, "SELECT * INTO #MyTemp FROM FooTable")
' Get results from table, e.g.
Dim intCount As Integer = sqlNET.ExecuteScalar(CommandType.Text, "Select Count(*) from #MyTemp")
MsgBox(intCount)
' Flag transaction as successful (causes a commit when scope is disposed)
oTranScope.Complete()
End Using ' Disposes the connection
End Using ' If this point is reached without hitting the oTranScope.Complete - the transaction is rolled back and locks, if any, are released.
如果要取出事务范围,则由于表不再存在,代码将在选择计数(*)上失败。指定范围可在命令调用之间保持连接打开。
我希望这有助于某人。
尼尔。
答案 1 :(得分:2)
您可以尝试使用全局临时表(即在查询中使用##MyTemp
而不是#MyTemp
),with this caveat:
全球临时表是 会话时自动删除 创建表结束和所有 其他任务已停止引用 他们。任务之间的关联 并且仅为表维护表 单个Transact-SQL的生命周期 声明。这意味着全球化 临时表被丢弃了 完成最后一个Transact-SQL 积极的声明 在引用表时 创建会话结束。
编辑:哎呀,错过了你已经尝试过全局临时表的事实。
如何将所有逻辑移动到一个存储过程中,该过程创建/填充临时表,然后运行查询并将多个结果集返回给客户端代码?
答案 2 :(得分:1)
您的问题缺少的是创建的表的生命周期。如果你会坚持一段时间,那么它不是一个临时表,它是你填充和使用的工作表。我根本不会使用临时表,只是一个由SELECT INTO创建并由其他人使用的常规表,直到它被删除(如果有的话)。
答案 3 :(得分:0)
我成功使用的另一种方法是在TempDb中创建一个工作表,并将其用作全局临时表(例如,“TempDb.dbo.MyTable”)。请记住,SQL Server重新启动时会删除用户表。