我有这个linq查询查询一个表,该表保留对任何表所做的任何更改历史记录构造如下:
History (Primary Key) | Table_Name | Primary_Key | Field_Name | Old_Value | New_Value | Action_Type | Action_Date | Action_User
我查询此表的linq查询是这样的:
var complaintHistory = from histories in DbContext.Histories
where ((from c in taskIds
select "<Task_ID=" + c + ">").Contains(histories.Primary_Key) ||
(from c in notificationListIds
select "<Notification_List_ID=" + c + ">").Contains(histories.Primary_Key) ||
(from c in emailSentIds
select "<Email_Sent_ID=" + c + ">").Contains(histories.Primary_Key) ||
histories.Primary_Key == "<Complaint_ID=" + Complaint.Complaint_ID + ">") &&
histories.Field_Name != "rowversion"
select histories;
每当我调用此查询时,我都会有严重的延迟。可以重写这个linq查询以显着提高性能,还是我试图在没有桨的情况下上一条小溪?
答案 0 :(得分:3)
当性能不好时,首先要查看生成的SQL。你会发现它太可怕了。它包含许多UNION
个单行表,通过这些表构建字符串from c in taskIds select "<Task_ID=" + c + ">"
等。
请记住,整个查询都会转换为SQL。如果你只有......
where taskIds.Contains(histories.Primary_Key)
......等等,然后这些谓词可以翻译成IN
语句。在您的查询中,组合字符串"<Task_ID=" + c + ">"
必须在笨重的SQL中构建。这足以拉下查询优化器。此外,taskIds
中的元素数量会极大地影响性能,并且不需要很多元素(~50)来触及最大嵌套级别。
首先要尝试的是首先通过自己的方式来缓解数据库引擎构建这些组合字符串:
var task_Ids = (from c in taskIds
select "<Task_ID=" + c + ">").ToArray();
和
where task_Ids.Contains(histories.Primary_Key)
其他两个ID列表相同。
如果taskIds
不包含太多元素(不是千),您还可以构建一个包含三个字符串组合的列表:
var ids = (from c in taskIds select "<Task_ID=" + c + ">")
.Union(
(from c in taskIds select "<Notification_List_ID="" + c + ">"))
.Union(
(from c in taskIds select "<Email_Sent_ID=" + c + ">"))
.ToArray();
并且只使用一个Contains
语句。
更重要的是,我认为你不应该以这种形式存储主键。它混合了数据和特定的表示实现(这本身就足够邪恶),并且它不必要地使查询复杂化。如果Primary_Key
是一个简单的值,等于原始记录,可能在一个复合键中,其类型字段决定Task
,Notification
等,这将会容易得多。
答案 1 :(得分:1)
正如人们所提到的,CR可以为您提供有关性能的全面评论。但这是第一次捅它。
从您的linq查询,它看起来像这样的直接检查:
&#39;给我所有的历史记录,其中字段名称不是&#39; rowversion&#39;主键是(投诉ID或电子邮件发送ID列表,通知ID或任务ID)&#39;
从linq-to-sql的角度来看,&#39;包含&#39;子句是唯一棘手的部分,但LInq-to-sql足够聪明,可以将其转换为带有值的SQL IN子句。
所以最终的SQL查询应该进行3 IN检查。不应该成为SQL的问题。
并且只要 Primary_key 列被索引(从名称开始,我假设它是,因为主键是群集索引的)并且可能是&#39; Field_name&#39;列,查询看起来很好。
尝试在SQL事件探查器中捕获查询,并在SQL管理工作室中运行它并查看执行计划。