【问题标题】:How can I speed up my Entity Framework LINQ Query?如何加快我的实体框架 LINQ 查询?
【发布时间】:2017-07-14 13:39:13
【问题描述】:

对不起,这样一个通用问题.. 我正在使用实体和 LINQ 来查询一个名为 Patient. 的表(模型)我的目标是使用 JqueryUI 的自动完成功能来协助 ASP.Net MVC 5 建议的搜索文本栏,您可以在其中可以使用字符串作为患者姓名,也可以在同一框中使用他们的 Key。这是我的模型:

public partial class Patient
{
    public Patient()
    {
        PickupExchange = new HashSet<PickupExchange>();
        SalesOrder2 = new HashSet<SalesOrder2>();
    }

    public int PtKey { get; set; }
    public int? PatientId { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string BillingAddress1 { get; set; }
    public string BillingAddress2 { get; set; }
    public string BillingCity { get; set; }
    public string BillingState { get; set; }
    public string BillingZip { get; set; }
    public string BillingPhone { get; set; }
    public string Faxnumber { get; set; }
    public string EmailAddress { get; set; }
    public string MobilePhone { get; set; }
    public string Suffix { get; set; }
    public DateTime? Dob { get; set; }
    public string Ssn { get; set; }
    public string HoldAcct { get; set; }
    public string AccountNumber { get; set; }
    public string AccountGrp { get; set; }
    public string Sex { get; set; }
    public decimal? Height { get; set; }
    public decimal? Weight { get; set; }
    public string MaritalStatus { get; set; }
    public string Employment { get; set; }
    public byte? DiscountPct { get; set; }
    public byte? BranchKey { get; set; }
    public string DelivAddress1 { get; set; }
    public string DelivAddress2 { get; set; }
    public string DelivCity { get; set; }
    public string DelivState { get; set; }
    public string DelivZip { get; set; }
    public string DelivPhone { get; set; }
    public string User1 { get; set; }
    public string User2 { get; set; }
    public string User3 { get; set; }
    public string User4 { get; set; }
    public string EclastName { get; set; }
    public string EcfirstName { get; set; }
    public string EcmiddleName { get; set; }
    public string Ecaddress1 { get; set; }
    public string Ecaddress2 { get; set; }
    public string Eccity { get; set; }
    public string Ecstate { get; set; }
    public string EcpostalCode { get; set; }
    public string EcphoneNum { get; set; }
    public string Ecfax { get; set; }
    public string Ecemail { get; set; }
    public string Rprelationship { get; set; }
    public string RplastName { get; set; }
    public string RpfirstName { get; set; }
    public string RpmiddleName { get; set; }
    public string Rpaddress1 { get; set; }
    public string Rpaddress2 { get; set; }
    public string Rpcity { get; set; }
    public string Rpstate { get; set; }
    public string RppostalCode { get; set; }
    public string RpphoneNum { get; set; }
    public string Rpfax { get; set; }
    public string Rpemail { get; set; }
    public string HippasignatureOnFile { get; set; }
    public string TaxZone { get; set; }
    public string BillingCounty { get; set; }
    public string DeliveryCounty { get; set; }
    public int? ReferralKey { get; set; }
    public string ReferralType { get; set; }
    public short? CreatedBy { get; set; }
    public DateTime? CreateDt { get; set; }
    public string NickName { get; set; }
    public string AutoChargeCc { get; set; }
    public string SarautoPayStatus { get; set; }
    public string SareDeliveryStatus { get; set; }
    public string SarpaymentPlan { get; set; }
    public string Sarinformation { get; set; }
    public DateTime UpdateDt { get; set; }
    public DateTime? Dod { get; set; }
}

问题是我的包含所有患者数据的 SQL 表绝对是巨大的。我正在使用我的 LINQ 查询将 TOP 10 作为 json 返回给我的 javasript 函数:

 var alreadyRunning = false;
 patientID.bind('input', function () {
    if (alreadyRunning == false) {
        alreadyRunning = true;
        $.ajax({
            type: 'POST',
            url: '/Controller/Model',
            dataType: 'json',
            data: {
                patientID: patientID.val()
            },
            success(data) {
                patientID.autocomplete({
                    source: data
                });
                alreadyRunning = false;
            }
            , failure(data) {
                alert("Error Searching Value: " + patientID.val());
                alreadyRunning = false;
            }
            , error(data) {
                alert("Error Searching Value: " + patientID.val());
                alreadyRunning = false;
            }
        });
    }
    else {
        patientID.autocomplete({
            source: ["None"]
        });
    }

此 LINQ 查询一直超时,我找不到加快查询速度的方法。是我做错了什么还是我的数据集太大?

  ptTable = int.TryParse(patientID, out number) ?
        btd.Patient.Where(x => x.Database == "databaseName" && x.PatientId.ToString().StartsWith(number.ToString())).OrderBy(x => x.LastName).Take(10).ToList() :
        btd.Patient.Where(x => x.Database == "databaseName" && x.LastName.ToLower() + ", " + x.FirstName.ToLower()).StartsWith(patientID.ToLower()).OrderBy(x => x.LastName).Take(10).ToList();    

SQL 是否需要太多的字符串操作来快速执行?任何建议都会非常有益!

【问题讨论】:

  • 你是否拿了 10 也没关系,因为 LINQ 无论如何都会先获取所有列表。
  • @Valkyriee 不。如果您在应用排序之前不执行查询 - EF 将生成 SQL 查询,该查询将在服务器端完成所有工作
  • 我可以确认这一点,.ToLower().ToString().StartsWith() 至少在我的数据库(SQL Server)上都转换为一个查询。 MySQL 的行为可能不同。
  • 是的,这肯定是太多的字符串操作。主要问题是您在搜索之前转换数据库值,这会禁用索引。我认为至少第一个查询应该替换为范围搜索。
  • @johnny5 如果我可以控制数据库,我很想对其进行重组。 : /

标签: c# mysql asp.net entity-framework linq


【解决方案1】:

SQL (MySQL) 服务器的问题是它按行存储数据。它序列化每一行数据并分配内部行ID。像这样的东西(来自维基百科):

001:10,Smith,Joe,40000;
002:12,Jones,Mary,50000;
003:11,Johnson,Cathy,44000;
004:22,Jones,Bob,55000;

在您的情况下,每一行将包含很多列。基于行的系统适用于返回整行数据。但是当您需要过滤数据时它们效率不高,因为应该将整行从磁盘加载到内存中,然后才能分析所需的字段。而基于行的系统会为整个表格做到这一点。

您可以通过为查询中使用的列创建索引来提高性能。例如。对于Database 字段(可能还有其他字段)。

ALTER TABLE Patient ADD INDEX COLUMN_NAME (COLUMN_NAME)

在这种情况下(再次来自 wiki)某些列的索引将如下所示

001:Smith;
003:Jones;
002:Johnson;
004:Jones;

因此,数据库不会从磁盘加载所有行,而是仅加载索引数据。这要快得多。

如果创建索引后性能仍然很差,请考虑使用面向列的数据库。

【讨论】:

  • 这是非常好的建议。不幸的是,我无法控制我可以访问的制作不佳的数据库。 : /
  • @Rinktacular 如果它不能解决你的问题,你为什么要把它标记为答案
  • @johnny5 我要求更快的查询,这个答案提供了。
  • @Rinktacular 如果您无法修改数据库,那么您可以修改的唯一内容就是发送到数据库的查询。您的示例非常简单,因此我怀疑 EF 会为简单的 where + orderby + take 查询生成一些低效的东西。但是您可以使用 Dapper 之类的东西来执行原始 sql。尽管如此,它不会给你带来良好的性能提升。最好问问那些管理数据库的人来为你创建索引
【解决方案2】:

显然您正在寻找一种创建自动完成功能的方法,但查询速度太慢,而且您无法控制数据库。在这种情况下,您应该将所有姓名和患者编号存储在内存中,即使您有大量数据,您仍然应该能够很好地缓存这些数据。

var lookupData = context.Patients.Select(p =>new { p.LastName, p.patientId}).ToArray();

var lastNames = lookupData.Select(x => x.LastName).ToArray();
var patientId = lookupData.Where(x => null != x.patientId).ToArray(); 

一旦你有了这些集合,你就可以过滤这个:

这样您只需进行一次查询,但是您必须在添加/删除/更新患者时添加一个机制来更新缓存,但至少您只需执行一次此查询

【讨论】:

  • 如果你有一个庞大的数据集并且你担心通过这么长的枚举来搜索,你可以先将它们分组
  • 非常感谢,这是更好的解决方案。
猜你喜欢
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
相关资源
最近更新 更多