如何创建连接本地的临时表,而无需强制使用简单的显式创建表语句?
Posted
技术标签:
【中文标题】如何创建连接本地的临时表,而无需强制使用简单的显式创建表语句?【英文标题】:How to create a temporary table that's local to the connection, without being forced to use a simple explicit create table statement? 【发布时间】:2015-04-28 02:08:48 【问题描述】:我想使用“select into #tempTable”形式的语句创建 SqlConnection 本地的临时表,因为我不必指定列名和类型,就像使用“create table # tempTable" 语句;
我希望临时表从在同一个已打开的 SqlConnection 上运行的后续 SqlCommand 可见,并且仅在该连接上可见。我不想使用全局临时表,因为我不想在查询同时运行时处理名称冲突。
我发现本地临时表只有在使用没有 SQL 参数的简单、单独的“create table #tempTable”语句创建时对后续的 SqlCommand 可见。另一方面,如果它是使用带有参数的“select into”语句创建的(或与单个 SqlCommand 中的其他语句串在一起),那么这会导致整个事情在一个过程中执行,这限制了 temp 的范围该过程的表。
一般来说,当 SqlCommand 不是没有参数的简单“创建表”语句时,似乎不可能跨 SqlCommand 使用临时表,因为 SqlCommand 在内部是如何工作的。
单独创建临时表会起作用(即一个简单的“创建表#tempTable(col 类型、col 类型等)”语句,没有 SQL 参数),但这涉及显式指定所有列类型(这也是大量维护,IMO),而使用“select into”语句,我不必指定列类型,因为它们会自动与源列匹配。
只要连接打开,我希望使用“select into”创建的临时表持续存在。有没有办法将表范围限定为打开的连接?
就目前而言,这确实是不可预测的行为,因为根据 SqlCommand 的构造方式,它可能会将自己包装在存储过程中,也可能不包装,因此在 SqlCommand 中创建的临时表可能对后续操作可见,也可能不可见SqlCommands 在同一个打开的连接上运行。
【问题讨论】:
不是 100% 确定我理解您所说的“在过程中执行”是什么意思,但是如果您需要多个连接来访问临时表,为什么不使用全局临时表呢?选择 ##globalTemp 因为全局临时表会导致与同时运行的所有其他命令实例发生名称冲突。 这是在存储过程还是动态 sql 中?我感觉它是动态的,这意味着您可以标记 sql 并共享一个唯一的表名。 它在 .NET 中带有一个 SqlCommand 对象。它有点像一个黑盒子,从某种意义上说,我不知道它是直接发出语句,还是将它们作为动态 SQL 运行。我觉得答案是“它取决于 sql 查询文本”,这就是问题所在。我需要一种方法来确保表的范围仅限于打开的连接,而不是特定的过程(+子过程)。 它不使用多个连接,它在同一个已经打开的 SqlConnection 上使用多个 SqlCommand 对象。问题是“new SqlCommand(“create table #tempTable”),将在后续的 SqlCommands 中可见,但类似“new SqlCommand("select * into #tempTable from X; select * from #tempTable;")” 将导致在过程中执行的语句中(作为动态SQL?),使其在最后被删除并且对同一连接上的后续SqlCommands不可见。这完全取决于创建表时传递给SqlCommand的文本的样子. 【参考方案1】:正如 cmets 中所指出的,您正在将 C# 中的 SQL 编码为 C# 字符串。因此,您可以使用任何您想要的名称创建临时表。
我将把实现细节留给你,但是这个片段应该可以帮助你:
public function DoSomeSqlStuff()
var tempTableName = CreateTempTableName();
using SqlConnection connection = new SqlConnection(connectionString)
using SqlCommand firstCommand = new SqlCommand("CREATE TABLE #" + tempTablename + "column definitions here", connection)
firstCommand.ExecuteNonQuery();
using SqlCommand secondCommand = new SqlCommand("SELECT * FROM #" + tempTableName, connection)
secondCommand.ExecuteReader();
using SqlCommand thirdCommand = new SqlCommand("DROP TABLE #" + tempTableName, connection)
thirdCommand.ExecuteNonQuery();
当然是一个简单的例子 - 但应该让你走上正确的道路。
至于连接范围的临时表作为数据库提供的一项功能......我从未听说过这样的事情。并不意味着它们不存在。
【讨论】:
为此,它们必须是全局临时表,所以我假设你的意思是有两个“##”哈希标签。在这种情况下,它们必须保证是唯一的,所以我必须使用 GUID 标识符,它仍然不能保证唯一性,即使它在实践中也是如此。这种方法的唯一问题是它会生成具有唯一表名的唯一查询字符串,这可能会阻止在 SQL Server 中缓存命令字符串,从而导致不必要的性能损失。我最终只是硬着头皮发出了明确的“创建表”语句。 在最后一条评论中没有字符......谢谢你的回答。这基本上就是这样的解决方案的样子。我不确定 SQL Server 是否缓存查询字符串,因此不必每次都解析它们,但我发出的查询字符串相当长,因此不必使用唯一的表名可以提高性能。跨度> 只是为了进一步讨论 - 您只需要在任何特定连接生命周期内“保证唯一性”。您说您只针对连接发出 2 个命令。如果我们大方地说每个查询需要 1 分钟来运行 - 您只需要每个表名一次唯一 2 分钟。 GUID 对此有点矫枉过正——但非常易于使用。根据建立连接的频率,Random.Next
可能“足够好”,但我不推荐它。
为了使用全局临时表名而不冒冲突的风险,绝对需要 GUID(或等效的唯一名称)。 “任何特定连接生命周期的持续时间”是 100% 的时间。从任何连接打开到该连接关闭,即使同时打开一个其他连接并尝试运行相同的查询,也会发生名称冲突。因此,无论连接持续时间如何,100% 的时间都需要保证名称的唯一性。 Random.Next 是不够的。你需要一个静态变量的 Interlocked.Increment。
@Triynko : 临时表的范围是每个连接以上是关于如何创建连接本地的临时表,而无需强制使用简单的显式创建表语句?的主要内容,如果未能解决你的问题,请参考以下文章