MS SQL 服务器:在准备好的语句中访问本地临时 table

MS SQL Server: Access a local temporary table in a prepared statement

问题陈述

我正在尝试创建并填充一个本地临时 table,它现在运行良好,但之后我无法通过 pepared 查询访问 table 及其内容。

问题的一个非常简化的版本,它已经重现了问题(C++0x with Qt5 framework):

// fyi: this constructor overload already executes the given query string
QSqlQuery query1("IF EXISTS(SELECT * FROM SYSOBJECTS WHERE ID=OBJECT_ID('#tmp') AND XTYPE='U') DROP TABLE #tmp", _db);
QSqlQuery query2("SELECT * INTO #tmp FROM mytable WHERE 1 = 0", _db);

// so far, so good: the table gets dropped if it was already existing and gets recreated with the schema of 'mytable'; it is empty due to the '1 = 0'

// this works: some code for inserting data
QSqlQuery query3("INSERT INTO #tmp SELECT mytable WHERE ...", _db);

// this also works: i can select some data
QSqlQuery query4("SELECT count(*) FROM #tmp WHERE col = 1234", _db);
Q_ASSERT(query4.hasNext());

// fails at exec() with error: "unkown object name '#tmp'"
QSqlQuery query5(_db);
query5.prepare("SELECT count(*) FROM #tmp WHERE col = :boundVar");
query5.bindValue(":boundVar", 1234);
query5.exec();

我在这里想念什么?我已经很难获得创建临时 table 到 运行 的查询,直到我在 SQL Server documentation:

中读到这个

In SQL Server 2005, the prepared statements cannot be used to create temporary objects [...], such as temporary tables. These procedures must be executed directly.

这已经很令人困惑了,因为我使用的是 SQL Server 2008r2 - 而为 Server 2012 编写的文档只提到了 2005 版本和更早版本。尽管如此,我尝试在没有准备的情况下执行创建 table 的查询并且它有效(参见代码示例)。然而,引述并没有说明通过准备好的语句访问临时table。

要求

由于性能原因,我必须使用本地临时文件 table:相当大的数据集将被过滤和转换并插入临时文件 table,其中大约有 10 个查询将跟随并使用这个具体化的中间数据。 TVP 不是一个好的选择,因为它会大大减慢以下查询的速度。在我的案例中,创建临时 table 的开销确实得到了好几次回报。此外,本地临时 table 比其他解决方案更受欢迎,因为它们与其他连接隔离并且在断开连接后将被清理,这正是我想要的。

有很多原因导致我不能没有阅读 table 的准备好的陈述,所以我真的希望有人能帮助我。

真题

如何使用准备好的查询从我的本地临时 table(当然是从创建它的同一连接中)读取数据?

非常感谢 idea/solution!

如何将所有这些都放在一个存储过程中,存储过程会创建一个真正的 table,前缀为日期和时间,以及用户 ID。

大致如此

创建TABLEsomeuser_190320151830.....

如上所述构建动态 sql 语句以填充 table

SELECT 来自 table 的任何内容,以便您的应用可以使用它

丢弃 TABLE someuser_190320151830

最后一步可能是 select 数据然后删除 table。

除非同一个人设法 运行 它可以在同一时间工作。我使用过类似的方法,但它很少运行。

具体解决方案

经过多次测试和实验,并根据 user1683641 and Tab Alleman 的想法,我将所有内容组合成最终可以完成工作并满足我所有要求的东西:

  • 我现在正在使用全局临时表,因为它们在非准备查询和准备查询范围之间可见
  • 当客户端应用程序在没有显式 DROP 的情况下关闭时,即使它被终止或连接丢失,也会清理全局临时表
  • 我用唯一的 GUID 为这些表添加后缀,这样可以避免用户和时间戳组合的冲突

最小示例:

QString const tableName(QString("##tmp_%1").arg(QUuid::createUuid().toString()));

// clean up, prepare empty table and fill with intermediate data
QSqlQuery query1(QString("IF EXISTS(SELECT * FROM SYSOBJECTS WHERE ID=OBJECT_ID('%1') AND XTYPE='U') DROP TABLE %1").arg(tableName), _db);
QSqlQuery query2(QString("SELECT * INTO %1 FROM mytable WHERE 1 = 0").arg(tableName), _db);
QSqlQuery query3(QString("INSERT INTO %1 SELECT mytable WHERE ...").arg(tableName), _db);

// works now! I can now select the temporary data perfectly fine from prepared statements
QSqlQuery query4(_db);
query4.prepare(QString("SELECT count(*) FROM %1 WHERE col = :boundVar").arg(tableName));
query4.bindValue(":boundVar", 1234);
query4.exec();

感谢大家的参与!我会接受 user1683641 的回答,因为它引导我这样做。