【问题标题】:Cursor Not Returned Query if Execute SQL Statement with Return Value如果执行带返回值的 SQL 语句,则游标不返回查询
【发布时间】:2014-10-20 02:16:22
【问题描述】:

我在使用 DBExpress 和 Delphi XE 执行以下查询时遇到问题。我需要从执行的查询中获取最后一个身份 ID:

function TServerDBUtils.ExecuteQueryWithIdentity(ASQLConn: TSQLConnection): Integer;
var
  newSQLQuery: TSQLQuery;
begin
  Result := -1;  
  newSQLQuery := TSQLQuery.Create(nil);
  try
    with newSQLQuery do
    begin
      SQLConnection := ASQLConn;

      SQL.Clear;
      SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')';

      SQL.Add('Select Scope_Identity()');
      Open;
      Result:= Fields[0].AsInteger;     
    end;
  finally
    FreeAndNil(newSQLQuery);
  end;
end;

我收到错误“光标未返回查询”。我以前使用过同样的方法,使用 FireDac 和 Delphi XE5 并且没有错误。不,我想知道在 DBExpress 中是否不允许“开放”做这样的事情。我应该使用什么方法? (我们必须在我们的项目中使用 DBExpress) 我试过这个:

function TServerDBUtils.ExecuteQueryWithIdentity(ASQLConn: TSQLConnection): Integer;
var
  newSQLQuery: TSQLQuery;
begin
  Result := -1;  
  newSQLQuery := TSQLQuery.Create(nil);
  try
    with newSQLQuery do
    begin
      SQLConnection := ASQLConn;

      SQL.Clear;
      SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')';
      ExecSQL;

      SQL.Clear;
      SQL.Add('Select Scope_Identity()');
      Open;
      Result:= Fields[0].AsInteger;     
    end;
  finally
    FreeAndNil(newSQLQuery);
  end;
end;

而且总是得到空值,可能是因为不同的会话。 抱歉我的英语不好,提前感谢您的帮助。

更新: 如果我们使用 @@identity 就可以了:

SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')';
ExecSQL;

SQL.Clear;
SQL.Add('Select @@Identity');
Open;
Result:= Fields[0].AsInteger; 

但是,正如 SQLServer 所说,如果在该表上触发了触发器(在插入时),则返回值是插入触发器的表的最后一个 ID。

【问题讨论】:

  • 使用存储过程,会让你的生活更轻松。

标签: sql-server delphi dbexpress


【解决方案1】:

在 SQL-Server 上最优雅的方式可能是使用 OUTPUT Clause 在多部分插入的情况下,它不仅能够返回一个 ID,而且能够返回所有新生成的 ID。

INSERT into aTable (aField)
OUTPUT Inserted.ID 
Values ('SomeValue')

如果您的表上有触发器,则必须为您的 OUTPUT 定义一个目标表

DECLARE @tmp table (ID int)
INSERT into aTable (aField)
OUTPUT Inserted.ID into @tmp 
Values ('SomeValue')
Select * from @tmp

另一个建议是使用参数而不是硬编码值。

使用 TSQLQuery 在语句前添加 SET NOCOUNT ON 将防止 cursor not returned query 错误并提供预期结果:

begin
  SQLQuery1.SQL.text :='SET NOCOUNT ON'
                +#13#10'DECLARE @tmp table (ID int)'
                +#13#10'INSERT into aTable (aField)'
                +#13#10'OUTPUT Inserted.ID into @tmp'
                +#13#10'Values (:P)'
                +#13#10'Select * from @tmp';
  SQLQuery1.Params.ParamByName('P').Value := 'SomeText';
  SQLQuery1.Open;
  Showmessage(SQLQuery1.Fields[0].asString);
end;

【讨论】:

  • 谢谢,问题仍然是 TSQLQuery 它自己。我们必须使用 TSQLQuery.Open 从查询中检索任何返回。但是 TSQLQuery.Open 不允许我们执行插入、更新等操作。我们会得到错误“光标未返回查询”。而 TSQLQuery.ExecSQL 可以执行插入、更新等操作,但没有任何返回值。如果我们使用 ExecSQL 后跟 Open,由于范围不同,它不起作用。 CMIIW
  • 非常感谢,'SET NOCOUNT ON' 是解决方案。我们已经在我们的 SQL Server 连接属性中进行了尝试,并使“NO COUNT”选中 = true。但它不起作用。但是当我们使用您的解决方案时:在主 SQL 之前添加“SET NOCOUNT ON”,然后是 Select scope_identity(),此方法有效。我们也会尝试您的建议,我们将尝试添加输出 Inserted.ID 并在我们的类中生成。
【解决方案2】:

我过去常常使用 XE2 和 SQL Server 来做到这一点

with newSQLQuery do
try      
  SQLConnection := ASQLConn;
  SQL.Clear;
  SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')');
  ExecSQL;

  SQL.Clear;
  SQL.Add('Select Scope_Identity() as id');
  Open;

  if not Eof then
     Result := FieldByName('id').asInteger;
finally
  FreeAndNil(newSQLQuery);
end;

所以,我在第二条 SQL 语句中引入了 as 关键字。当我打开查询时,只需检查请求的名称为“id”的字段。

【讨论】:

  • 谢谢,我已经尝试过你的解决方案,但返回值始终为 0。可能是因为 ExecSQL 和 Open 是同一个会话,但范围不同。所以 scope_identity() 不起作用。如果我们使用@@identity,我们会得到结果,因为它会读取任何范围,但是如果表中存在插入其他表的触发器,我们会得到错误的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-23
  • 2020-04-17
  • 1970-01-01
  • 1970-01-01
  • 2014-01-15
  • 2012-12-19
相关资源
最近更新 更多