如果TADOQuery查询中有参数,临时表将会丢失

4

我有一个TADOQuery,如果我硬编码“Where参数”,它可以生成临时表并正常工作,但是如果我使用TADO参数,下一次查询就不知道这个临时表了。

我做错了什么?

我希望我能简化这个例子,但是这就是它。 (SQL Server)

    CREATE TABLE brFTNode_Children ( 
      pID integer NOT NULL, 
      cID integer NOT NULL, 
      primary key (pID, cID)
    );

    insert into brFTNode_Children values(1,2);
    insert into brFTNode_Children values(1,3);
    insert into brFTNode_Children values(3,4);
    insert into brFTNode_Children values(3,5);
    insert into brFTNode_Children values(6,4);
    insert into brFTNode_Children values(6,7);

代码(无法工作)

procedure Foo(fDBCon : TADOConnection);
const
    CreateTempTable =
                  'WITH FT_CTE AS( ' +
                  'SELECT pID, cID FROM brFTNode_Children ' +
                  'WHERE pID = :TOPID ' +
                  'UNION ALL ' +
                  '  SELECT e.pID, e.cID FROM brFTNode_Children e ' +
                  '  INNER JOIN FT_CTE ftCTE on (ftCTE.cID = e.pID)) ' +
                  'SELECT *  INTO #ParentChild FROM FT_CTE; ';


    GetSQL =
                  'SELECT pID, cID  FROM #ParentChild ORDER BY pID; ';
var
  q1  : TADOQuery; 
  q2  : TADOQuery;

begin
  q1 := TADOQuery.Create(nil);
  q1.Connection := fDBCon;
  q1.SQL.Text := CreateTempTable;
  q1.ParamCheck := True;
  q1.Parameters.ParamByName('TOPID').DataType := ftInteger;
  q1.Parameters.ParamByName('TOPID').Value := 1;
  q1.ExecSQL;

  q2 := TADOQuery.Create(nil);
  q2.Connection := fDBCon;
  q2.SQL.Text := GetSQL;
  q2.Active := true; //Fails here does not know table #ParentChild
end;

代码 - 在SQL查询中使用常量

function TGenerateSolveFile.GetBinaryStream(    topID  : Cardinal;
                                            var bFile: TMemoryStream): Boolean;

const
    CreateTempTable =
                  'WITH FT_CTE AS( ' +
                  'SELECT pID, cID FROM brFTNode_Children ' +
                  'WHERE pID = 1 ' + //Changed To a constant
                  'UNION ALL ' +
                  '  SELECT e.pID, e.cID FROM brFTNode_Children e ' +
                  '  INNER JOIN FT_CTE ftCTE on (ftCTE.cID = e.pID)) ' +
                  'SELECT *  INTO #ParentChild FROM FT_CTE; ';


    GetSQL =
                  'SELECT pID, cID  FROM #ParentChild ORDER BY pID; ';
var
  q1  : TADOQuery;  
  q2  : TADOQuery;

begin
  q1 := TADOQuery.Create(nil);
  q1.Connection := fDBCon;
  q1.SQL.Text := CreateTempTable;
//  q1.ParamCheck := True;
//  q1.Parameters.ParamByName('TOPID').DataType := ftInteger;
//  q1.Parameters.ParamByName('TOPID').Value := 1;
  q1.ExecSQL;

  q2 := TADOQuery.Create(nil);
  q2.Connection := fDBCon;
  q2.SQL.Text := GetSQL;
  q2.Active := true;
end;

1
为什么需要执行两个查询?你尝试过简单地使用 q2.SQL.Text := CreateTempTable + GetSQL; 吗?无论如何,我会为此创建一个单独的存储过程。将参数传递给它并获取记录集。 - kobik
1个回答

4

参数化查询使用exec sp_executesql,它有自己的会话。

您可以从分析器中获取此信息。

exec sp_executesql N'WITH FT_CTE AS( SELECT pID, cID FROM brFTNode_Children WHERE pID = @P1 UNION ALL   SELECT e.pID, e.cID FROM brFTNode_Children e   INNER JOIN FT_CTE ftCTE on (ftCTE.cID = e.pID)) SELECT *  INTO #ParentChild FROM FT_CTE; 
',N'@P1 int',1

如果您在 SSMS 中执行此操作,然后调用 select * from #ParentChild,您将收到相同的错误。

sp_executesql (Transact-SQL)

sp_executesqlEXECUTE 具有相同的行为,涉及批处理、名称范围和数据库上下文。在 sp_executesql@stmt 参数中的 Transact-SQL 语句或批处理只有在执行 sp_executesql 语句时才会被编译。然后,@stmt 的内容将被编译并作为一个执行计划单独执行,而不是调用 sp_executesql 的批处理的执行计划的一部分。 sp_executesql 批处理无法引用在调用 sp_executesql 的批处理中声明的变量。在 sp_executesql 批处理中的本地游标或变量对调用 sp_executesql 的批处理不可见。更改数据库上下文仅持续到 sp_executesql 语句的结束。


有没有办法让另一个查询使用相同的会话。我不想为每个需要它的查询运行临时查询,也不能创建真实表,因为有多个线程执行相同的代码。还是有其他解决方案吗? - runfastman
sp_executesql执行完成后会丢失会话,您可以使用名称由适应GUID构建的全局临时表来避免与其他用户发生冲突。这些表将在最后一个用户断开连接后被删除,例如##F7BBE50B_9406_4A35_9FAD_6C54D317A390。顺便说一句:问题准备得很好。 :) - bummi
我找到了一个解决方案,感觉有点羞愧。在执行查询之前,我只需替换字符串中的ID即可。简单的解决方案。 - runfastman
@bummi,我认为没有必要使用##table显式地指定GUID。SQL服务器会为您完成这项工作。 - kobik
@kobik 不确定我们是否在思考同一件事情:SQL Server中的本地和全局临时表 - bummi
@bummi,很酷。CREATE TABLE ##t将创建一个全局临时表。表本身已经有一个GUID了。正如你所提到的:“这些将在最后一个用户断开连接后被删除”。我可能误解了你的意思。我的意思是,您不需要显式指定GUID。+1 - kobik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接