多个语句Delphi TZquery(Zeos)错误

Reb*_*lss 4 mysql delphi zeos delphi-xe3

我试图像这样做一个多语句查询:

// without the second insert the query works fine.
// i need 2 querys to work because later, i'll do inserts on different kind of tables.
// that's why i need 2 querys, not a single query which insert 2 records.   

with ZQuery1 do
    begin
        SQL.Clear;
        SQL.Add('insert into client (name,age) values ('+QuotedStr('john')+','+QuotedStr('20')+');');
        SQL.Add('insert into client (name,age) values ('+QuotedStr('doe')+','+QuotedStr('21')+');');
        ExecSQL;
    end;
Run Code Online (Sandbox Code Playgroud)

我收到此错误消息:SQL错误:您的SQL语法中有错误; 检查与您的MySQL服务器版本相对应的手册,以便在第2行的'insert into client(name,age)values('doe','21')'附近使用正确的语法;

我已经查看了手册,组件TZQuery和TZUpdateSql(来自zeos lib)提供了在内部执行多个语句的可能性.

编辑[求助]

谢谢GregD,经过多次测试,交易对我来说很好!这就是我如何使用,以帮助将来的其他人.

try
    ZConnection.AutoCommit := True;
    ZConnection.StartTransaction;

    With ZQuery Do
    begin
        SQL.Clear;
        SQL.Add('insert into clients (name,age) values ('+QuotedStr('john')+','+QuotedStr('20')+')');
        ExecSQL;
        SQL.Clear;
        SQL.Add('insert into clients (name,age) values ('+QuotedStr('doe')+','+QuotedStr('21')+')');
        ExecSQL;
    end;

    ZConnection.Commit; 
except
    ZConnection.Rollback
end;
Run Code Online (Sandbox Code Playgroud)

这就是AutoCommit属性在Zeos中的实际工作方式:

AutoCommit为True时,则在每个执行的SQL语句之后自动提交事务,但是您可以明确地使用StartTransaction命令来阻止此自动提交,直到您明确地调用Commit.

AutoCommit为False时,您不应该调用StartTransaction.然后事务自动启动,但在每个执行的语句后都不会自动提交.

procedure StartTransaction StartTransaction过程在连接的数据库中启动新事务.它应该仅在AutoCommit属性为TRUE时使用.每当您尝试使用AutoCommit设置为false调用它时,将引发SInvalidOpInNonAutoCommit.此行为是预期的,因为StartTransaction应该用作AutoCommit模式的转义.当您调用StartTransaction时,AutoCommit将"关闭",然后,当您调用Commit或Rollback时,AutoCommit将再次"打开".如果您使用AutoCommit设置为false,则会自动创建新事务,并选择关闭它们的方式(Commit或Rollback).

procedure Commit将当前语句提交到数据库.应仅在非AutoCommit模式下使用(其中每个语句都是自动提交的,使此过程无效),或者当您处于AutoCommit模式并希望完成StartTransaction过程打开的事务时.提交完成当前事务,如果有的话.如果您不想将satatements保存到数据库,则应使用Rollback过程.

procedure Rollback回滚当前事务中的所有先前语句.应仅在非AutoCommit模式下使用(其中每个语句都是自动提交的,使此过程无效),或者当您处于AutoCommit模式并希望完成StartTransaction过程打开的事务时.Rollback完成当前事务,如果有的话.如果您不想丢失您的声明,则应使用Commit过程.

Ken*_*ite 5

我不知道Zeos和多个语句,但这不是真正的问题.您的查询(SQL注入)和执行它们的缓慢方法(无法缓存和重用的连接字符串)导致了严重的安全问题.

如果您正确地停止使用字符串连接来形成查询,并使用参数化语句,则根本不需要担心多个语句:

with ZQuery1 do
begin
  SQL.Clear;
  SQL.Add('insert into client (name,age)');
  SQL.Add('values (:Name, :Age);'
  ParamByName('Name').AsString := 'John';
  ParamByName('Age').AsInteger := 20;
  ExecSQL;
  ParamByName('Name').AsString := 'Doe';
  ParamByName('Age').AsInteger :- 21;
  ExecSQL;
end;
Run Code Online (Sandbox Code Playgroud)

查询现在运行得更快(因为DBMS可以编译一次并重复使用多次(我提到的"缓存"),您不再具有SQL注入风险,并且不再需要多个语句.


Gre*_*egD 2

尝试此代码,如果出现同样的问题,请告诉我们:

with ZQuery1 do
begin
    SQL.Clear;
    SQL.Add('insert into client (name,age) values ('+QuotedStr('john')+','+QuotedStr('20')+'),('+QuotedStr('doe')+','+QuotedStr('21')+');');
    ExecSQL;
end;
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您还可以加快 MySQL 处理该INSERT查询的速度,因为它是在一批中完成的,而不是两次。

编辑#1:

我不是Zeos专家,但对于其他语言,你可以尝试一一执行查询:

with ZQuery1 do
    begin
        SQL.Clear;
        SQL.Add('insert into client (name,age) values ('+QuotedStr('john')+','+QuotedStr('20')+');');
        ExecSQL;
        SQL.Clear;
        SQL.Add('insert into client (name,age) values ('+QuotedStr('doe')+','+QuotedStr('21')+');');
        ExecSQL;
    end;
Run Code Online (Sandbox Code Playgroud)

编辑#2交易

Stackoverflow 上的一个问题有很多关于在 MySQL 中使用事务的好例子。虽然这些示例是为 PHP 编写的,但我相信您可以在那里找到一些很好的指导。确保 MySQL 服务器上的表InnoDB不是MyISAM.

  • @Rebelss,为什么不使用事务呢?事务符合 ACID,并确保执行完全完成。 (3认同)
  • @Rebelss我不是Zeos专家,但从概念来看,我不认为多次插入查询可以解决连接丢失的问题。相反,就像 GregD 所建议的那样,使用事务似乎是最合适的。事务到位后,即使它们是多个执行语句,也应该无关紧要。 (2认同)