“basic” Entity query结果非常慢,这样做有什么问题?

huangapple go评论68阅读模式
英文:

What's wrong on do this "basic" Entity query which result very slow?

问题

这是我的代码:

var activities = ctx.Activities.AsNoTracking();
var data = ctx.Patients.AsNoTracking().Select(p => new
{
    ID = p.ID,
    DateOfBirth = p.DateOfBirth,
    FirstName = p.FirstName,
    LastName = p.LastName,
    Gold = p.Gold,
    PromoSmiles = p.PromoSmiles,
    PatientStatusID = p.PatientStatusID,
    Activity = activities.Where(a => a.PatientID == p.ID).GroupBy(a => a.Converted).Select(a => a.FirstOrDefault()).ToList(),
    Clinic = p.Clinics
}).AsEnumerable().Select(p => new
{
    ID = p.ID,
    ClinicID = p.Clinic.ID,
    Name = p.LastName + " " + p.FirstName,
    Clinic = p.Clinic.Name,
    Date = p.DateOfBirth.HasValue ? p.DateOfBirth.Value.ToString("yyyyMMdd") : "-",
    PatientStatusID = p.PatientStatusID,
    ActivityTypeID = (p.Activity.Count == 0 ? (int?)ActivityType.NESSUNA : p.Activity.Count > 1 ? (int?)ActivityType.ENTRAMBE : p.Activity.FirstOrDefault().Converted.HasValue && p.Activity.FirstOrDefault().Converted.Value ? (int?)ActivityType.PRATICA : (int?)ActivityType.VISITA).ToString(),
    Gold = p.Gold.HasValue && p.Gold.Value ? 1 : 0,
    PromoSmiles = p.PromoSmiles.HasValue && p.PromoSmiles.Value ? 1 : 0
});

// 这里我将来会在数据上应用一些进一步的筛选和排序

var dataTotal = data.Count();
if (formData.length >= 0)
{
    data = data.Skip(formData.start).Take(formData.length);
}
var dataFiltered = data.ToList();
return Json(new { data = dataFiltered, recordsTotal = dataTotal });

希望对你有所帮助。

英文:

Here's my code:

var activities = ctx.Activities.AsNoTracking();
var data = ctx.Patients.AsNoTracking().Select(p => new
{
	ID = p.ID,
	DateOfBirth = p.DateOfBirth,
	FirstName = p.FirstName,
	LastName = p.LastName,
	Gold = p.Gold,
	PromoSmiles = p.PromoSmiles,
	PatientStatusID = p.PatientStatusID,
	Activity = activities.Where(a => a.PatientID == p.ID).GroupBy(a => a.Converted).Select(a => a.FirstOrDefault()).ToList(),
	Clinic = p.Clinics
}).AsEnumerable().Select(p => new
{
	ID = p.ID,
	ClinicID = p.Clinic.ID,
	Name = p.LastName + " " + p.FirstName,
	Clinic = p.Clinic.Name,
	Date = p.DateOfBirth.HasValue ? p.DateOfBirth.Value.ToString("yyyyMMdd") : "-",
	PatientStatusID = p.PatientStatusID,
	ActivityTypeID = (p.Activity.Count == 0 ? (int?)ActivityType.NESSUNA : p.Activity.Count > 1 ? (int?)ActivityType.ENTRAMBE : p.Activity.FirstOrDefault().Converted.HasValue && p.Activity.FirstOrDefault().Converted.Value ? (int?)ActivityType.PRATICA : (int?)ActivityType.VISITA).ToString(),
	Gold = p.Gold.HasValue && p.Gold.Value ? 1 : 0,
	PromoSmiles = p.PromoSmiles.HasValue && p.PromoSmiles.Value ? 1 : 0
});

// here's I'll apply in future some further filters and orders on data

var dataTotal = data.Count();
if (formData.length >= 0)
{
	data = data.Skip(formData.start).Take(formData.length);
}
var dataFiltered = data.ToList();
return Json(new { data = dataFiltered, recordsTotal = dataTotal });

When I execute it (Patients got nearly 10k records) it takes 5-6 seconds to be executed, which is very long.

Not sure where am I wrong; I've noticed that data.Count() do a query with "all fields" of the tables, not the ones I've selected above, so maybe the lag is also because of the streaming "to server" all the records before doing count?

Also, it seems that Activity extraction could be improved maybe? I've tried some others tuning, but its takes always the same time.

Last thing: .AsEnumerable() seems overkill, but not sure why I should replace it getting Data formatted from SQL as I want. Maybe would help make custom view?

I would expect to be executed in few ms this basic query, right?

答案1

得分: 2

> AsEnumerable()

只要你在 IQueryable<> 上操作,查询仍然在构建中,并且将在准备好时一次性发送。然而,当你使用上述代码将其强制转换为 IEnumerable<> 时,查询将按原样发送到服务器,运行然后下载所有数据供你在Linq中随意使用。

稍后应用的任何分页操作 (Take/Skip) 将在整个数据库的内存副本上运行,而不是在服务器上使用索引。

英文:

> AsEnumerable()

As long as you work on an IQueryable<> the query is still being constructed and will be sent when ready all at once. However when you force it to become an IEnumerable<> with that above line the query as is gets sent to the server, it runs and then downloads all of the data for you to use in Linq as you wish.

Any paging you apply later (Take/Skip) will be run on the in-memory copy of the entire database, not on the server using indices.

答案2

得分: 0

很明显,没有您的数据库架构,我们无法确切地说出原因。可能是任何原因。

但是,我会尝试一个基于经验的猜测:

您在“患者”上没有“活动”导航属性的事实使我相信,无论您是使用代码优先还是数据库优先,可能都没有在“活动”的“PatientID”上存在外键。因为它只会通过代码优先的导航属性或数据库优先的自动创建导航属性才会出现。没有外键,存在一个突然出现的孤立随机索引的可能性也很小。这很可能意味着对于每个患者,活动表都要进行完整的表扫描。这是慢的。

解决方法?通过您用于创建数据库的任何方式,将[Activity].[PatientID]设置为外键,将其连接到[Patient].[ID]的主键,并确保它在上下文中反映出来,如果您没有使用代码优先。

英文:

Obviously, without your database schema we cannot really tell. Could be anything.

However, I will try an educated guess:

The fact that you have no navigation property of "Activities" on "Patient" leads me to believe that whether you are using code first or database first, there probably is no foreign key on "PatientID" of "Activities". Because it would only get there through the navigation property in code first, or a navigation property would have been created automatically with database first. With no foreign key, the chance of there being a lone random index that just magically appeared is also slim. That most likely means for each patient, the activities table gets a full table scan. That is slow.

The solution? Give [Activity].[PatientID] a foreign key connecting it to the primary key of [Patient].[ID] through whatever means you use to create your database and make sure it is reflected in your context if you aren't using code first.

huangapple
  • 本文由 发表于 2023年3月7日 23:25:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/75663915.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定