同时使用多个ExecuteReader

时间:2012-04-19 11:03:41

标签: c# performance sqlite ado.net system.data.sqlite

我正在使用ExecuteReader()命令读取包含一些表的数据库。 基于我在两个不同表中读取的第一个Read()结果的结果,因为我需要在第一个查询上返回的ID来运行第二个。

问题是这种搜索速度非常慢。

tuCommand.CommandText = "SELECT * FROM tblTranslationUnit WHERE DocumentId = " + doc.DocumentId;
var tuReader = tuCommand.ExecuteReader();
while (tuReader.Read())
{
    var tu = new TranslationUnit
     {
         TranslationUnitId = tuReader.GetInt64(0),
         DocumentId = tuReader.GetInt64(1),
         Raw = tuReader.GetString(2),
         IsSegmented = tuReader.GetBoolean(3),
         Reader = this, // Ryan: Fixed so that it sets the reader to itself
     };

    using (var propCommand = _dbConn.CreateCommand())
    {
        propCommand.CommandText = "SELECT * FROM tblTranslationUnitProperties WHERE TranslationUnitId = " + tu.TranslationUnitId;
        var propReader = propCommand.ExecuteReader();
        while (propReader.Read()) tu.Properties.Add(GetProperty(propReader));
    }
    yield return tu;
}

如果删除第二个ExecuteReader(),查询真的很快

我还尝试使用新连接和新事务放置第二个ExecuteReader(),但结果几乎相同

任何想法或线索?我该怎么做这种搜索?还有更好的方法吗? (我想是的)。


更多细节 db结构是:

  - Document
      - properties
      - errors
    -TranslationUnits
        - properties
        - errors
      - Segments
          - properties
          - errors

因此,在代码的某些部分,我们将拥有此结构

  foreach (document in db)
      foreach (property in document)
      foreach (error in document)
    foreach (translationunit in document)
        foreach (property in translationunit)
        foreach (error in translationunit)
      foreach (segment in translationunit)
          foreach (property in segment)
          foreach (error in segment)

基于此,使用连接返回所有内容并不是一个好主意 我在想这个问题是否只是一个SQLite配置问题。我的意思是,如果可以添加任何参数或类似参数告诉系统我们将使用几个指针

现在我们正在转向数据表解决方案:

  1. 打开连接
  2. 读取表格的1000个条目
  3. 关闭连接
  4. 打开新连接
  5. 阅读子表的1000个条目
  6. 关闭新连接
  7. ...

5 个答案:

答案 0 :(得分:1)

听起来你有可扩展性问题。 SQLite中有“Lite”这个词是有原因的。它缺乏某些东西,如高并发性,细粒度访问控制,丰富的内置函数集,存储过程,深奥的SQL语言特性,XML和/或Java扩展,tera或peta字节可伸缩性等等。我建议为初学者更改数据库。

我也不清楚你的问题为什么你需要在内存中同时拥有1000个文件,特别是1000个文件,其中1000个零件,1000个零件,全部都在内存中。我不知道您的UI要求,但在我15年以上的编程中,我不记得曾经在一个网页或表单上显示1000个没有任何分页机制的东西,所以你真的需要得到一次性从数据库中获得1000 * 1000 * 1000个实体?

我认为你需要再看看UI,当前模型和数据层,以寻找在不牺牲大量性能的情况下尽可能少地提供内容的方法。考虑使用Lazy Loading,预读缓冲区,缓存,分页,搜索方法,共享静态数据等模式来降低前期成本。

考虑买房子。我们大多数人都没有钱支付所有房子,所以我们得到了抵押贷款。抵押贷款是随着时间的推移分摊前期成本的方式。所有抵押贷款都会带来负面影响。现在,我没有支付100,000美元,而是因为我可以负担当前的付款,但我并没有真正注意到额外的150,000,因为随着时间的推移,额外的成本会以很小的增量吸收。另请注意,如果我在5年内卖掉房子而不是终身贷款,我甚至可能无法偿还全部250,000。

这里的要点是,您可以分摊额外连接以检索较小记录集的成本,并仍然为用户提供他们现在所需的内容。这将降低总体前期成本,但会为正在检索的各个记录集增加额外成本。

答案 1 :(得分:1)

嗨,我要加上我的发现(我和大卫一起)

我使用缓冲区修改了我们从db读取表的方式,因为david描述的是没有同时连接,也没有读取器同时执行。它似乎有点快,但有点明显。这是一些数字。

我在2.5秒内使用5000个翻译单元填充数据库(所有表格)。 然后,当我遍历TranslationUnit表(大约5000行)时,阅读时间非常壮观:0.07秒。代码如下:

foreach (var tu in document)
{
   ... do something ...
}

如果我像这样阅读每个翻译单元的细分:

foreach (var tu in document)
{
    foreach (var seg in tu)
    {
        ... do something ...
    }
}

阅读时间开始变得丑陋:大约10秒钟。 请注意,每个翻译单元都有2个段(尽管我们不在设计中对此进行限制)

对于10000个翻译单元,填充数据库大约需要6秒钟,大约需要2分钟才能读取。 (如果只有1个foreach阅读翻译单位,几乎是即时的)

对于50000翻译单元,它需要大约32秒才能填充,并且在等待读数完成1小时后我放弃了。 (如果只有1个foreach阅读翻译单位,几乎是即时的)

所以我的猜测是阅读时间成本呈指数级增长。认为这是因为它必须将数据库指针更改为不同的表是否合理? (在翻译单位和细分表之间)。

答案 2 :(得分:0)

你试过一个简单的“加入”吗?或者我在你的问题中遗漏了什么?

SELECT tbl2.* 
    FROM tblTranslationUnit tbl1 
    JOIN tblTranslationUnitProperties tbl2 ON tbl2.TranslationUnitId = tbl1.TranslationUnitId 

答案 3 :(得分:0)

首先,您可以使用join编写select,并使用一个查询获取所有内容

SELECT * FROM tblTranslationUnit join tblTranslationUnitProperties on
tblTranslationUnitProperties.TranslationUnitId = tblTranslationUnit.id 
WHERE DocumentId = @docID //<= use parameter

如果没有帮助,可能需要索引表格。

答案 4 :(得分:0)

立即读取第一个查询的所有结果,关闭DataReader,然后在内存中枚举结果。