【问题标题】:Improve query performance in mvvm application提高 mvvm 应用程序中的查询性能
【发布时间】:2018-01-03 14:50:33
【问题描述】:

我有一个记录电话查询和建议的 wpf mvvm 应用程序。

我正在尝试查询通话记录以获取相关实体中的信息,但性能很慢。

Call--1:M--CallQuery--M:M--AnswerDoc--1:M--AnswerDocSection
                    1:M|M:1
                CallQueryAnswerDoc

因此,调用将包含一个或多个查询(查询是一个问题及其答案),并且(答案)一个查询将引用 1 个或多个(最多 2 个)AnswerDocsSections。每个 AnswerDocSection 都来自一个 AnswerDoc。创建 CallQueryAnswerDoc 表是为了处理 CallQuery 和 AnswerDoc 表之间的 M:M 联接。

为了准备一个常见问题解答,我正在尝试查询哪些调用使用了某些 AnswerDocs 和/或 AnswerDocSections。

对于我的第一种方法,我检索了一个 ObservableCollection 的呼叫,然后循环遍历每个呼叫以收集其 CallQueries 的 ObservableCollection,并且对于每个 CallQuery,检查 AnswerDocID 是否与 searchText 的 AnswerDocID 匹配。如果是这样,则将调用添加到返回的调用列表中。

在大约 5k 个调用的数据库中,这需要十多分钟,并且以每 10 秒大约 100 个调用的速度进行。

所以,接下来我尝试使用如下语法在 linq 查询中完成所有操作:

var qCQADS = callList.Where(c => 
               c.CallQueries.Any(cq => 
                 cq.CallQueryAnswerDocs.Any(cqad => 
                   cqad.AnswerDocSectionID == CallSearch.AnswerDocSectionID)));

但令人惊讶的是(对我来说,无论如何)这似乎需要与newing ObservableCollections 一样多的时间。

我可以在 sql 中执行此查询并在 1 秒内获得结果。

SQL 查询

SELECT dbo.AnswerDoc.Code, dbo.AnswerDocSection.Title, dbo.Call.CallID,
       dbo.Call.CallDate, dbo.Call.RegionID, dbo.Call.CallSubject
FROM dbo.AnswerDocSection
INNER JOIN dbo.CallQueryAnswerDocSection ON dbo.AnswerDocSection.AnswerDocSectionID = 
  dbo.CallQueryAnswerDocSection.AnswerDocSectionID
INNER JOIN dbo.CallQuery ON dbo.CallQueryAnswerDocSection.CallQueryID = 
    dbo.CallQuery.CallQueryID
INNER JOIN dbo.AnswerDoc ON dbo.AnswerDocSection.AnswerDocID = dbo.AnswerDoc.AnswerDocID
INNER JOIN dbo.Call ON dbo.CallQuery.CallID = dbo.Call.CallID
WHERE (dbo.AnswerDoc.Code = N'General')
  AND (dbo.AnswerDocSection.Title = N'Invalid Username')

如何使用 wpf 实现这种性能?

【问题讨论】:

  • 5000 行是 nothing,您确定您的相关表上有索引吗?在处理如此少的数据时,我通常更喜欢将 all 数据加载到我的应用程序中的缓存对象中,以避免往返数据库。
  • 同意@Robert 的建议,您可以使用Include,如here 所述。
  • @Robert :所有 ID 都是主键。正如我所说,在 Sql Server 中运行 sql 查询需要不到 1 秒的时间。 ObservableCollection 的调用被传递给我的方法(linq 示例中的 callList),我的代码只是循环通过集合。我最初是后端,我认为这方面还可以。
  • @KeyurPATEL 我认为您可能正在做某事(令人震惊的是,我认为我之前在 SO 上犯过这个错误)。你想让你的评论成为答案吗?
  • 在我所有的Include() 经验中,我从来不需要超过 2 个级别的深度,不过我会尽力而为 :)

标签: c# sql-server linq


【解决方案1】:

我不能 100% 确定语法,因为我没有靠近计算机来测试它。另外,我以前从未需要Include() 嵌套实体的深度超过 1 级,但这里是:

您可以首先将所有内容加载到本地内存中(正如 Robert 在 cmets 中建议的那样,对于较少的行数应该没问题,例如 5000 左右)。

var qCQADS_all = callList.Include(x => x.CallQueries.Select(q => q.CallQueryAnswerDocs)).ToList();

var qCQADS_all = callList.Include("CallQueries.CallQueryAnswerDocs").ToList();

然后您可以执行嵌套的Any(),这应该比“十分钟以上”花费的时间要少得多。

qCQADS = qCQADS_all.Where(c => 
           c.CallQueries.Any(cq => 
             cq.CallQueryAnswerDocs.Any(cqad => 
               cqad.AnswerDocSectionID == CallSearch.AnswerDocSectionID)));

【讨论】:

  • 是的,检索调用的原始查询没有include 相关项目。结果现在在约 10 秒内填充。比我预期的要多一点,但我很高兴将此归咎于 wpf。非常感谢。
  • 理想情况下,我知道您希望进一步减少时间。如果是这样,您可以将CallQueriesCallQueryAnswerDocs 加载到内存中,然后使用老式的foreach 循环,而不是整个Include() 块。我个人使用 EntityFramework,它还允许我在数据库上运行 SQL 查询并检索记录,这对你的情况非常有帮助,因为你已经知道 SQL 需要大约 1 秒。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-03
  • 1970-01-01
  • 1970-01-01
  • 2010-11-12
  • 2016-08-18
相关资源
最近更新 更多