【问题标题】:Multiple statements Delphi TZquery (Zeos) error多语句 Delphi TZquery (Zeos) 错误
【发布时间】:2013-05-26 12:45:59
【问题描述】:

我正在尝试像这样进行多语句查询:

// 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;

我收到此错误消息:SQL 错误:您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 2 行的“插入客户端(名称,年龄)值('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;

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

AutoCommit 为 True 时,事务会在每次执行 SQL 语句后自动提交,但您可以显式使用 StartTransaction 命令来防止这种自动提交,直到您显式调用 Commit。

AutoCommit 为 False 时,不应调用 StartTransaction。然后事务会自动启动,但不会在每条执行语句后自动提交。

过程 StartTransaction StartTransaction 过程在连接的数据库中启动一个新事务。它应该只在 AutoCommit 属性为 TRUE 时使用。每当您尝试在 AutoCommit 设置为 false 的情况下调用它时,都会引发 SInvalidOpInNonAutoCommit。这种行为是意料之中的,因为 StartTransaction 应该用作对 AutoCommit 模式的转义。当您调用 StartTransaction 时,AutoCommit 将“关闭”,然后,当您调用 Commit 或 Rollback 时,AutoCommit 再次“打开”。如果您将 AutoCommit 设置为 false,则会自动创建新事务,您可以选择关闭它们的方式(提交或回滚)。

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

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

【问题讨论】:

  • OT:如果您拥有 Delphi 或 RAD Studio 的 Enterprise、Ultimate 或 Architect 版本,请尝试FireDAC
  • 如果没有第二个插入,这是否有效?
  • 是的,没有第二个插入它就像一个魅力:)
  • 我一直想知道是谁发起了这种丑陋的SQL.Clear; SQL.Add(...); 模式而不是更短的SQL.Text := '...'。像所有愚蠢的东西一样,它非常耐用。

标签: mysql delphi delphi-xe3 zeos


【解决方案1】:

我不知道 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;

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

【讨论】:

  • +1,就您解决 OP 问题以及其他潜在问题而言,这是最佳答案。
  • 就速度而言,这是否等于insert into client (name,age) values(:Name1, :Age1), (:Name2, :Age2)
  • @SAMPro:视情况而定。你必须自己测试它。 SQL 在第一次执行后被编译和缓存,所以唯一会改变的部分是参数的值。我怀疑这会很快,特别是如果你只插入几行。如果你做了很多行,我认为创建 SQL 和参数并填充它们的性能问题也会有点慢。像往常一样,答案是:自己对它们进行基准比较,使用您自己的数据和硬件,然后确定最适合您的。
  • 我怀疑 MySQL 方面的性能。使用您的解决方案,例如 MySQL 需要多次锁定表,但如果您准备并执行一个查询,它会快得多。你说得对,插入几行并没有什么不同。
  • @SAMPro:正如我所说(并将为您重复):像往常一样,答案是:自己对它们进行基准比较,使用您自己的数据和硬件,并决定哪个效果最好给你.
【解决方案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;

通过这种方式,您还可以加快 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;

编辑#2交易

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

【讨论】:

  • 您的答案可行且很好,但问题是关于使用多个插入语句...但是很好尝试:)
  • 是的,但他的 DBA 可能更喜欢多行 INSERT 语句;-)
  • 好点。我同意。实际上,我自己也有这个问题,并且不确定 Zeos 是否支持多个查询。我现在没有在我的 Linux 操作系统上测试它。是否可以使用多个查询?只是好奇。谢谢!哦,为您的回答 +1。
  • @Rebelss,为什么不使用事务呢?交易符合 ACID,并确保执行完全完成。
  • @Rebelss 我不是 Zeos 专家,但从概念上讲,我认为多次插入查询不能解决连接丢失的问题。相反,就像 GregD 建议的那样,使用 Transaction 似乎是最合适的。并且有了事务,即使它们是多个执行语句也不重要。
【解决方案3】:

我也不是 ZEOS 方面的专家,但看看 the source,你是否将 TZUpdateSQLMultiStatements 属性设置为 true?

【讨论】:

    【解决方案4】:

    你试过 TZSQLProcessor 吗?表示该组件是为这样的需求而制作的(如在 ZSqlProcessor.pas 单元中):

    {**
      Implements a unidatabase component which parses and executes SQL Scripts.
    }
    

    【讨论】:

    • 你救了我的命。我一直在寻找类似的东西,但文档很差
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-08
    • 2018-07-20
    • 2021-11-16
    • 2019-04-20
    • 1970-01-01
    相关资源
    最近更新 更多