2

我正在尝试对创建时间非常长的结果集进行许多不同的查询。为了获得性能提升,我希望使用一个临时表并在这个临时表上做很多查询。

看起来很标准。然而,我正在努力在动态 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
4

4 回答 4

6

我知道这个帖子发布已经有一段时间了,但我相信答案很简单。

我猜您正在使用 MS 企业库来访问数据库,这解释了为什么命令之间不存在临时表。当命令完成时,企业库明确关闭与数据库的连接(将其放回池中)。也就是说,除非您将命令放入事务中。如果您直接使用 ADO.NET(通过打开连接、构建和执行命令,然后关闭连接),您不会遇到此问题(连接关闭时由您自己决定——风险更大)。这是一些使用 MS 企业库和事务(对不起,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.

如果您要取出事务范围,代码将在 Select count(*) 上失败,因为表不再存在。指定范围使命令调用之间的连接保持打开状态。

我希望这可以帮助别人。

尼尔。

于 2010-09-02T09:43:48.163 回答
2

您可以尝试使用全局临时表(即,使用##MyTemp而不是#MyTemp在查询中),但需要注意的是

当创建表的会话结束并且所有其他任务停止引用它们时,将自动删除全局临时表。任务和表之间的关联仅在单个 Transact-SQL 语句的生命周期内维护。这意味着在创建会话结束时主动引用该表的最后一个 Transact-SQL 语句完成时将删除全局临时表。


编辑:哎呀,错过了您已经尝试过全局临时表的事实。

如何将所有逻辑移动到一个存储过程中,该存储过程创建/填充临时表,然后运行查询并将多个结果集返回给客户端代码?

于 2009-02-16T00:31:43.257 回答
1

您的问题缺少的是创建表的生命周期。如果你让它停留一段时间,那么它就不是一个临时表,它是一个你填充和使用的工作表。我根本不会使用临时表,只是一个由 SELECT INTO 创建并由其他人使用的常规表,直到它被删除(如果有的话)。

于 2009-02-16T00:46:55.217 回答
0

我成功使用的另一种方法是在 TempDb 中创建一个工作表,并像使用全局临时表一样使用它(例如,“TempDb.dbo.MyTable”)。请记住,SQL Server 重新启动时会删除用户表。

于 2009-06-16T18:54:28.437 回答