SQL 临时表在不同的 SQL 读取器之间共享

发布于 2024-07-13 21:02:51 字数 964 浏览 8 评论 0原文

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

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

I am trying to do many different queries on a result set which has a very large creation time. To get performance gains I wish to use a temp table and just do many queries on this temp table.

Seems pretty standard. Yet I am struggling to share this temp table in dynamic SQL. As I understand it, each SqlCommand object executes in its own thread and so the temp table is in a different scope - thus making it inaccessible from the query thread.

I tried using a global temporary table and that works great, but not ideal?

How can I share a local temporary table between dynamic SQL queries?

My intent:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

╰◇生如夏花灿烂 2024-07-20 21:02:51

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

我猜测您正在使用 MS Enterprise Library 来访问数据库,这解释了为什么命令之间不存在临时表。 当命令完成时,企业库明确关闭与数据库的连接(将其放回到池中)。 也就是说,除非您将命令放入事务中。
如果您直接使用 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(*) 上失败,因为表不再存在。 指定范围使命令调用之间的连接保持打开状态。

我希望这可以帮助别人。

尼尔.

I know it's a while since this one was posted but the answer, I believe, is quite simple.

I surmise you are using the MS Enterprise Library to access the database, this explains why the temp table doesn’t exist between commands. The Enterprise Library EXPLICITLY closes the connection to the DB (puts it back in the pool) when the command finishes. That is, UNLESS you put the commands into a transaction.
If you use ADO.NET directly (by opening the connection, building and executing the commands, then closing the connection) you do not get this problem (it’s up to you when the connection closes – which is more risky).
Here is some code written using the MS Enterprise Library and a transaction (sorry, 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.

If you were to take out the transaction scope, the code would fail on the Select count(*) as the table no longer exists. Specifying the scope keeps the connection open between command calls.

I hope this helps someone.

Neil.

感情废物 2024-07-20 21:02:51

您可以尝试使用全局临时表(即,在查询中使用 ##MyTemp 而不是 #MyTemp),有此警告

全局临时表是
会话时自动丢弃
创建表结束和所有
其他任务已停止引用
他们。 任务之间的关联
并且仅维护一个表
单个 Transact-SQLTransact-SQL 的生命周期
陈述。 这意味着全球
临时表被删除在
完成最后一个 Transact-SQLTransact-SQL
积极的声明
参考该表时
创建会话结束。


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

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

You could try using a global temporary table (ie, use ##MyTemp rather than #MyTemp in your queries), with this caveat:

Global temporary tables are
automatically dropped when the session
that created the table ends and all
other tasks have stopped referencing
them. The association between a task
and a table is maintained only for the
life of a single Transact-SQL
statement. This means that a global
temporary table is dropped at the
completion of the last Transact-SQL
statement that was actively
referencing the table when the
creating session ended.


EDIT: Oops, missed the fact that you've already tried global temp tables.

How about moving all of your logic into a single stored procedure which creates/populates the temp table and then runs the queries and returns multiple resultsets to the client code?

策马西风 2024-07-20 21:02:51

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

What is missing from your question is the lifecycle of the created table. If you will have it sticking around for a while, then it is not quite a temp table, it is a work table that you populate and use. I would not use a temp table at all, just a regular table that gets created by the SELECT INTO and used by everyone else until it gets dropped (if ever).

多孤肩上扛 2024-07-20 21:02:51

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

An alternative that I've used successfully is to create a work table in TempDb, and use it as if it is a global temp table (e.g., "TempDb.dbo.MyTable"). Remember that user tables are dropped when SQL Server reboots.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文