数据库中有数百万行,只需要

时间:2016-05-09 11:27:46

标签: c# .net oracle entity-framework large-data

问题摘要:

  • C#(MVC),实体框架5.0和Oracle。
  • 我在视图中有几百万行连接两个表。
  • 我需要使用过滤器功能填充下拉列表。
  • 这些下拉列表中的选项应反映实际内容 该列的视图,distinct。
  • 我想在你选择的时候更新下拉列表,所以 新选项反映过滤后的内容,阻止您 选择会产生0结果的东西。
  • 速度慢。

问题:获取这些下拉列表的正确方法是什么?

现在了解更多细节。

- 页面目标 -

向用户显示一些下拉列表,用于过滤下面网格中的数据。网格表示视图(参见“数据库”),其中结果被过滤。

每个下拉列表代表视图列的过滤器。选择某些内容后,页面的其余部分将更新。其他下拉列表现在包含其相应列的可用值,这些列符合刚刚在第一个下拉列表中应用的过滤器。

一旦用户选择了几个过滤器,他/她就会按下搜索按钮和下拉列表下方的网格更新。

- 数据库 -

我有一个视图,从两个表中选择几乎所有列,没有什么花哨的。像这样:

SELECT tbl1.blabla, tbl2.blabla etc etc
FROM table1 tbl1, table2 tbl2
WHERE bsl.bvz_id = bvz.id AND bsl.einddatum IS NULL;

共有22列。 13 VARCHARS(大多数小,1 - 20,其中一个大小为2000!),6个日期和3个数字(其中一个大小为38,其中一个为15,2)。

表上有几个索引,其中包括WHERE子句的相关ID。

要知道的重要事项:我无法更改数据库。也许在这里和那里设置一个索引,但没什么大不了的。

- 实体框架 -

我在我的解决方案中创建了一个数据库第一个EDMX,并且还映射了视图。这两个表都有类,但是我需要来自它们的数据,所以我不知道我是否需要它们。从两个表中选择事物的问题是你不能应用一半的过滤,但也许有一些我没想过的聪明方法。

- 查看

我的视图与viewModel绑定在一起。在那里,我为每个下拉列表都有一个IEnumerable。这些getter从一个名为NameOfViewObjects的IEnumerable中获取数据。像这样:

public string SelectedColumn1{ get; set; }

private IEnumerable<SelectListItem> column1Options;
public IEnumerable<SelectListItem> Column1Options
{
    get
    {
        if (column1Options == null)
        {
            column1Options= NameOfViewObjects.Select(item => item.Column1).Distinct()
            .Select(item => new SelectListItem
                  {
                       Value = item,
                       Text = item,
                       Selected = item.Equals(SelectedColumn1, StringComparison.InvariantCultureIgnoreCase)
                  });
        }
        return column1Options;
    }
}

我尝试过的两个解决方案是:

- 1 - 选择linq查询中的所有列,我需要下拉列表(2000 varchar不是其中之一,并且只有2个日期列),对它们执行不同的操作并将结果放入Hashset。然后我将NameOfViewObjects设置为指向此hashset。我必须等待大约2分钟才能完成,但在此之后,填充下拉列表几乎是即时的(每个人可能只需一秒钟)。

model.Beslissingen = new HashSet<NameOfViewObject>(dbBes.NameOfViewObject
                .DistinctBy(item => new
                    {
                        item.VarcharColumn1,
                        item.DateColumn1,
                        item.DateColumn2,
                        item.VarcharColumn2,
                        item.VarcharColumn3,
                        item.VarcharColumn4,
                        item.VarcharColumn5,
                        item.VarcharColumn6,
                        item.VarcharColumn7,
                        item.VarcharColumn8
                    }
                )
            );

这里的一个大问题是对象NameOfViewObject可能非常大,即使在这里使用distinct,导致少于100.000的结果,它仍然使用超过500mb的内存。这是不可接受的,因为会有很多用户使用这个屏幕(很多人会......最多10个,同时平均5个)。

- 2 - 另一个解决方案是使用相同的linq查询并将NameOfViewObjects指向它生成的IQueryable。这意味着每次视图要将下拉列表绑定到IEnumerable时,它都会触发一个查询,该查询将在具有数百万行的表中找到该列的不同值,其中很可能是从中获取值的列未被索引。每个下拉列表大约需要1分钟(我有10个),因此需要很长时间。

不要忘记:我需要在每次更改下拉列表时更新下拉列表。

- 问题 - 所以我可能会采用错误的方式,或者这些解决方案中的一个应该与我使用的所有列的索引相结合,也许我应该使用另一种方式将数据存储在内存中,所以它只是一点点但是必须有人在那之前做过这件事,并想出一些聪明的东西。你能否告诉我处理这种情况的最佳方法是什么?

可接受的表现:

  • 在页面加载时不得不等待一段时间(2分钟),但是 之后一切都很快。
  • 每次下拉列表都要等几秒钟 变化
  • 页面使用的内存不超过500mb

4 个答案:

答案 0 :(得分:3)

当然,您应该在WHERE子句中的所有列和组合上都有索引。没有索引意味着表扫描和O(N)查询时间。在任何情况下都无法扩展。

在下拉列表中需要数百万条目。您需要更聪明地将数据库过滤到可管理的条目数。

我会从Google获取一个页面。他们的类型有助于将整个互联网图表缩小为每页25或50个组,最有可能位于顶部。也许你也可以管理它。

也许更好的答案就像搜索引擎。如果您是Java开发人员,可以尝试Lucene / SOLR并编制索引。我不知道.NET的等价物是什么。

答案 1 :(得分:2)

您需要检查的第一点是您的数据库,确保您有正确的索引和实体关系,

接下来,如果您想动态构建过滤器选项,则需要使用现有过滤器运行查询以获取下一个过滤器的内容。有几种方法可以做到这一点,

首先,您可以查询数据并从返回中提取值,这会产生巨大的加载时间并浪费时间返回您不想要的数据(除非您使用过滤器实时更新结果并且没有分页,在哪种情况下,您可能只需获取所有数据并使用linqToObjects进行过滤)

第二个选项是对每个返回可能过滤器的过滤器进行并行查询,因此过滤器A =数据中A的所有可能值,过滤器b =在数据中按A过滤时B的所有可能值,C =当A&amp; amp;过滤时,C的所有可能值B在数据等方面,这比第一个好,但不是很多

另一种选择是使用聚合来加速,即你有一个如上所述的并行查询,但不是返回数据,而是返回返回多少记录,聚合函数总是更快,所以这会大大减少你的加载时间但是你仍然反复询问一个巨大的数据集,它不会是非常糟糕的。 你可以使用exists进一步调整它,只返回0或1。

在这种情况下,您将查看包含所有可能过滤器的表,然后从并行查询中删除没有值的那些

下一个选项将是最快的一英里是缓存数据库中的过滤器,具有单独的表 然后你可以查询并说出来自Cache,其中filter = ABC选择D,这就是维护缓存的问题,你必须在DB中做这些,作为保存函数,触发器等的一部分。

答案 2 :(得分:1)

除了以前的建议之外,可以添加的另一个解决方案是使用/*+ result_cache */提示,如果您的Oracle版本支持它(Oracle version 11g or later)。如果查询的输出对于下拉列表足够小,那么当用户输入的条件与另一个用户使用的条件匹配时,结果将在几毫秒而不是几秒或几分钟内返回。对于返回数百万行的少量行的查询,结果缓存非常有用。

select /*+ result_cache */ item_desc from some_table where item_id ...

当在数据库表上发生任何插入/更新/删除时,将自动刷新结果缓存。

答案 3 :(得分:0)

我在过去做过某种'类似'的事情 - 如果你可以在数据库中添加一个表格,那么我将探索引入一个'暂存器'类型表,其中结果会在用户优化搜索时临时存储。由于多个用户可能同时工作,因此表必须有一个额外的列来识别用户。

我认为您会看到一些性能优势,因为所有处理都保留在服务器端,而您的应用程序只是从此表中提取数据。由于您要添加此表,因此您也可以完全控制它。

基本上我认为程序流程会像:

  1. 用户选择一些过滤器并点击“搜索”。
  2. 服务器使用该搜索的结果填充暂存器表。
  3. 应用程序从暂存器表填充结果网格。
  4. 用户进一步优化搜索并点击“搜索”。
  5. 服务器根据需要删除/添加行到暂存器表。
  6. 应用程序从暂存器表填充结果网格。
  7. 等等。
  8. 不是让所有用户都在一个'scratchpad'表中,你可能会探索每个用户都有临时的'scratchpad'表。