【问题标题】:Why records added so slowly为什么记录添加如此缓慢
【发布时间】:2013-10-19 15:04:06
【问题描述】:

我正在从 SQL Server 2005 读取记录,并使用以下代码将返回的记录集写入 SQLite。

我的编译器是 Lazarus 1.0.12,qt1 是“sqlquery”,“qrystkmas”是 Zeos dbo 的 Ztable...

但是操作很慢。测试时间是

开始时间:15:47:11 结束时间:16:19:04 记录数:19500

因此,在 SQL Server 和 SQL Server CE 对中,Delphi 项目不到 2-3 分钟。

我怎样才能加快这个过程?

代码:

  Label2.Caption:=TimeToStr(Time);
  if Dm.Qt1.Active then Dm.Qt1.Close;
    Dm.Qt1.SQL.Clear;
    Dm.Qt1.SQL.Add('  select ');
    Dm.Qt1.SQL.Add('   st.sto_kod, st.sto_isim,st.sto_birim1_ad,  ');
    Dm.Qt1.SQL.Add('   st.sto_toptan_vergi,st.sto_perakende_vergi,');
    Dm.Qt1.SQL.Add('   st.sto_max_stok,st.sto_min_stok, ');
    Dm.Qt1.SQL.Add('   sba.bar_kodu,   ');
    Dm.Qt1.SQL.Add('   stf.sfiyat_fiyati  ');
    Dm.Qt1.SQL.Add('  from MikroDB_V14_DEKOR2011.dbo.STOKLAR st ');
    Dm.Qt1.SQL.Add('  left JOIN MikroDB_V14_DEKOR2011.dbo.BARKOD_TANIMLARI sba on sba.bar_stokkodu=st.sto_kod ');
    Dm.Qt1.SQL.Add('  left JOIN MikroDB_V14_DEKOR2011.dbo.STOK_SATIS_FIYAT_LISTELERI stf on stf.sfiyat_stokkod=st.sto_kod  ');
    Dm.Qt1.SQL.Add('  where LEFT(st.sto_kod,1)=''5'' --and stf.sfiyat_listesirano=1 ');
    Dm.Qt1.Open;
    Dm.qryStkMas.Open;
    Dm.qrystkmas.First;

    While not Dm.Qt1.EOF do
    begin
      Dm.qryStkMas.Append;
      Dm.qryStkMas.FieldByName('StkKod').AsString :=Dm.Qt1.FieldByName('sto_kod').AsString;
      Dm.qryStkMas.FieldByName('StkAd').AsString  :=Dm.Qt1.FieldByName('sto_isim').AsString;
      Dm.qryStkMas.FieldByName('StkBrm').AsString :=Dm.Qt1.FieldByName('sto_birim1_ad').AsString;
      Dm.qryStkMas.FieldByName('StkBar').AsString :=Dm.Qt1.FieldByName('bar_kodu').AsString;
      Dm.qryStkMas.FieldByName('StkKdv1').AsFloat :=Dm.Qt1.FieldByName('sto_toptan_vergi').AsFloat;
      Dm.qryStkMas.FieldByName('StkKdv2').AsFloat :=Dm.Qt1.FieldByName('sto_perakende_vergi').AsFloat;
      Dm.qryStkMas.FieldByName('StkGir').AsFloat  :=0;
      Dm.qryStkMas.FieldByName('StkCik').AsFloat  :=0;
      Dm.qryStkMas.FieldByName('YeniStk').AsBoolean :=False;
      Dm.qryStkMas.FieldByName('MinStk').AsFloat  :=Dm.Qt1.FieldByName('sto_min_stok').AsFloat;
      Dm.qryStkMas.FieldByName('MaxStk').AsFloat  :=Dm.Qt1.FieldByName('sto_max_stok').AsFloat;
      Dm.qryStkMas.FieldByName('StkGrp1').AsString:='';
      Dm.qryStkMas.FieldByName('StkGrp2').AsString:='';
      Dm.qryStkMas.FieldByName('StkGrp3').AsString:='';
      Dm.qryStkMas.FieldByName('StkGrp4').AsString:='';
      Dm.qryStkMas.FieldByName('StkFytno').AsInteger:=1;
      Label1.Caption:=Dm.Qt1.FieldByName('sto_isim').AsString;
      Dm.qryStkMas.Post;
      Dm.Qt1.Next;
    end;
    Dm.qryStkMas.Close;
   label3.Caption:=timetostr(time);

【问题讨论】:

  • 你用的是什么驱动?
  • freetds 和 lazarus 标准 mssqlconnection+sqlite 驱动和 zeos dbo...
  • @RBA 我没有看到任何需要参数的东西...
  • 澳洲联储; qrystkmas 是空的,所以打开没有问题,也不是瓶颈点。也没有连接到它的数据感知组件。这只是一个数据泵代码。
  • 我建议在调试中逐行逐行检查您的代码,并寻找需要时间的关键语句。

标签: sqlite delphi sql-server-2005 freepascal


【解决方案1】:

加快速度的第一步是诊断。

测量
您可以通过拆分选择和插入来衡量,但您也可以从 SQL 本身中获得一些诊断。

如果您在 SQLite 中使用关键字 EXPLAIN 作为查询前缀,它将告诉您使用了哪些索引以及如何在内部处理该语句,请参见此处:http://www.sqlite.org/eqp.html
这是优化的宝贵信息。
在 MS SQL Server 中,您进入 gui,输入查询并单击估计的查询计划按钮,请参阅:what is the equivalent of EXPLAIN form SQLite in SQL Server?

什么最花时间?是select 慢还是insert

选择
通常通过在那些被评估的字段上放置索引来加速选择。
在您的情况下,连接条件中涉及的字段。
where 子句中的字段使用函数,您不能在 MSSQL 中的函数上放置索引(您可以在 PostgreSQL 和 Oracle 中)。

插入
禁用索引可以加快插入速度。
一个常见的技巧是在插入批处理之前禁用所有索引,并在插入批处理完成后重新启用它们。
这通常更有效,因为它可以更快地(每个项目)一次性对整体进行排序,以便在每个单独的插入之后继续使用。

您还可以禁用交易保护。
这将损坏您的数据以防万一或电源/磁盘等故障,因此请考虑警告自己,请参阅此处:Improve large data import performance into SQLite with C#

代码注释
您使用 SQL 选择语句选择数据,但是您使用数据集 appendfieldbyname() 方法插入。众所周知,FieldByName 的速度很慢,因为它每次都会进行名称查找。
FieldByName 永远不应在循环中使用
改为构造insert SQL 语句。
请记住,您可以使用参数,甚至可以在其中硬粘贴值。
做一些实验,看看哪个更快。

关于如何通过消除FieldByName: http://delphi.about.com/od/database/ss/faster-fieldbyname-delphi-database.htm 来加快数据库访问速度,About.com 有一篇很好的文章

【讨论】:

  • 约翰;生病检查一下。但我现在害怕性能问题。我只想为 wince 开发一个应用程序,因为 Sqlite 是该操作系统(对于 lazarus)上 DBMS 的唯一选项,我真的对 wince 终端上的性能感到困惑。但我从我之前的测试中知道这一点:选择是在 2-3 秒内返回完整记录。但写作记录是半个多小时。我会阅读您建议的链接,看看我们能做些什么。
  • 您正在比较一个 win32 Delphi 应用程序和一个 Lazarus WINCE 应用程序之间的性能?是不是有点不公平,不应该在原帖中提到这个事实吗?
  • @MarcovandeVoort 我不是在比较他们像一对。我知道它们是两个不同的编译器和配置文件。但我知道 Lazarus 程序不能像这样慢,我肯定在这个项目中犯了一个错误。但是哪里 ? – ikutluay 13 分钟前
【解决方案2】:

您是否尝试将插入包装在事务中?您需要在您的 While... 之前开始一个事务,并在 ...End 之后提交它。试试吧,它可能会有所帮助。

编辑:如果您得到改进,那将是因为您与 SQLite 的数据库连接是在“自动提交”模式下设置的,其中每个操作(例如您的 .Append)都是与所有其他操作独立完成的,而 SQLite足够聪明,可以确保数据库的 ACID 属性。这意味着对于您进行的每次写入操作,数据库都会对您的硬盘进行一次或多次写入,这很慢。通过显式创建事务(关闭自动提交...),您可以将写入操作分组到一个事务中,当您显式提交事务时,数据库可以向硬盘发出更少的写入。

【讨论】:

  • 这应该是一条评论。这不是一个答案,因为它没有解释任何东西。
  • 我说,不用担心自动提交,在一条 SQL 语句中完成所有插入操作。
  • 我建议对插入进行分组并每 n 次插入执行一次显式提交可能会提供更好的性能,然后将其全部保留到最后。拥有未提交的事务可能会占用服务器资源、锁定数据库上的行或页面、减慢其他用户的速度,并且可能会导致其他争用。最好的建议是尝试定期提交(以及所有其他建议),看看有什么效果!
  • @IvanVoras 我禁用了自动提交,现在时间更长了,44 分钟...
  • 这完全出乎意料。您只是禁用了自动提交,还是将所有内容都包装在事务中?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-03
  • 2017-09-01
  • 2015-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多