C# LinqToSql查询返回时间太长性能太长
我正在执行一个相当强大的LinqToSql语句,它返回一个新对象。由于SQL方法的数量(主要是求和和和转换),SQL的运行时间非常长,因此加载网页需要很长时间(10-15秒)。而我可以使用AJAX或类似的CSS加载器。首先,我想知道是否有一种简单的方法来实现我试图从SQL数据库中获取的内容 我正在努力:C# LinqToSql查询返回时间太长性能太长,c#,sql-server,linq,linq-to-sql,C#,Sql Server,Linq,Linq To Sql,我正在执行一个相当强大的LinqToSql语句,它返回一个新对象。由于SQL方法的数量(主要是求和和和转换),SQL的运行时间非常长,因此加载网页需要很长时间(10-15秒)。而我可以使用AJAX或类似的CSS加载器。首先,我想知道是否有一种简单的方法来实现我试图从SQL数据库中获取的内容 我正在努力: 返回给定字段不为空的所有用户 获取opportunities表中状态为“open”且外键匹配的所有当前项。(手动连接后) 在这些机会中,将几个字段的所有货币值的总和存储到我的类中 获取这些货币价
decimal _default = (decimal)0.0000;
var users = from bio in ctx.tbl_Bios.Where(bio => bio.SLXUID != null)
join opp in ctx.slx_Opportunities.Where(opp => opp.STATUS == "open") on bio.SLXUID equals opp.ACCOUNTMANAGERID into opps
select new UserStats{
Name = bio.FirstName + " " + bio.SurName,
EnquiryMoney = opps.Where(opp => opp.SALESCYCLE == "Enquiry").Sum(opp => (opp.ACTUALAMOUNT.HasValue && opp.ACTUALAMOUNT.Value != _default ? opp.ACTUALAMOUNT : opp.SALESPOTENTIAL.HasValue ? (decimal)opp.SALESPOTENTIAL.Value : _default)).GetValueOrDefault(_default),
EnquiryNum = opps.Where(opp => opp.SALESCYCLE == "Enquiry").Count(),
GoingAheadMoney = opps.Where(opp => opp.SALESCYCLE == "Going Ahead").Sum(opp => (opp.ACTUALAMOUNT.HasValue && opp.ACTUALAMOUNT.Value != _default ? opp.ACTUALAMOUNT : opp.SALESPOTENTIAL.HasValue ? (decimal)opp.SALESPOTENTIAL.Value : _default)).GetValueOrDefault(_default),
GoingAheadNum = opps.Where(opp => opp.SALESCYCLE == "Going Ahead").Count(),
GoodPotentialMoney = opps.Where(opp => opp.SALESCYCLE == "Good Potential").Sum(opp => (opp.ACTUALAMOUNT.HasValue && opp.ACTUALAMOUNT.Value != _default ? opp.ACTUALAMOUNT : opp.SALESPOTENTIAL.HasValue ? (decimal)opp.SALESPOTENTIAL.Value : _default)).GetValueOrDefault(_default),
GoodPotentialNum = opps.Where(opp => opp.SALESCYCLE == "Good Potential").Count(),
LeadMoney = opps.Where(opp => opp.SALESCYCLE == "Lead").Sum(opp => (opp.ACTUALAMOUNT.HasValue && opp.ACTUALAMOUNT.Value != _default ? opp.ACTUALAMOUNT : opp.SALESPOTENTIAL.HasValue ? (decimal)opp.SALESPOTENTIAL.Value : _default)).GetValueOrDefault(_default),
LeadNum = opps.Where(opp => opp.SALESCYCLE == "Lead").Count(),
PriceOnlyMoney = opps.Where(opp => opp.SALESCYCLE == "Price Only").Sum(opp => (opp.ACTUALAMOUNT.HasValue && opp.ACTUALAMOUNT.Value != _default ? opp.ACTUALAMOUNT : opp.SALESPOTENTIAL.HasValue ? (decimal)opp.SALESPOTENTIAL.Value : _default)).GetValueOrDefault(_default),
PriceOnlyNum = opps.Where(opp => opp.SALESCYCLE == "Price Only").Count(),
ProvisionalMoney = opps.Where(opp => opp.SALESCYCLE == "Provisional").Sum(opp => (opp.ACTUALAMOUNT.HasValue && opp.ACTUALAMOUNT.Value != _default ? opp.ACTUALAMOUNT : opp.SALESPOTENTIAL.HasValue ? (decimal)opp.SALESPOTENTIAL.Value : _default)).GetValueOrDefault(_default),
ProvisionalNum = opps.Where(opp => opp.SALESCYCLE == "Provisional").Count()
};
您可以做很多事情:
CREATE NONCLUSTERED INDEX FI_OpenStatus_Opportunities
ON dbo.Opportunities (AccountManagerId, Status, ActualAmount)
WHERE status = 'OPEN';
GO
同样,对于您的总和和计数(每列一个):
(其余部分依此类推)
我不是说这是你最好的主意;但这是一个想法。它是否好取决于它在您的工作负载环境中的表现(我无法回答)
索引视图
您还可以创建包含此汇总信息的索引视图;这是一个稍微先进一点,取决于你
为此:
CREATE VIEW [SalesCycle_Summary] WITH SCHEMABINDING AS
SELECT AccountManagerID, Status, SUM(ActualAmount) AS MONETARY
,COUNT_BIG(Status) as Counts
FROM [DBO].Opportunities
GROUP BY AccountManagerID, Status
GO
-- Create clustered index on the view; making it an indexed view
CREATE UNIQUE CLUSTERED INDEX IDX_SalesCycle_Summary ON [SalesCycle_Summary] (AccountManagerId);
然后(取决于您的设置),您可以直接加入到该索引视图,或者通过提示包含它(尝试使用前者)
最后,如果这些都不起作用(关于索引视图有一些问题——我已经6个月没有使用它们了,所以我不太记得具体的问题了),您可以创建一个CTE并完全抛弃Linq To SQL
这个答案有点超出范围(因为我已经给出了两种方法,它们需要您进行大量调查)
要了解这些方法的作用,请执行以下操作:
将统计信息IO设置为ON
设置统计时间
- 选中“显示实际查询计划”和“显示预计查询计划”的框
- 将生成的SQL复制到其中;运行它
- Statistics IO告诉您查询进行的逻辑和物理读取的数量(越低越好——首先关注读取数量高的区域)
- 统计时间告诉您运行查询和向SSMS显示查询结果所用的时间(请确保启用
,以免影响结果)SET NOCOUNT
- 实际的查询计划会准确地告诉您它在使用什么,SQL Server认为您缺少什么索引,以及其他会影响结果的问题,如隐式转换或错误的统计信息,所以我不会在这里重复答案李>
- 估计的查询计划会告诉您SQL Server认为将要发生的事情——这些事情并不总是与实际的查询计划相同——并且您希望确保在调查中考虑到差异
这里没有“简单”的答案;答案取决于您的数据、数据使用情况以及您可以对基础架构进行的更改。一旦您在SSMS中运行它,您将看到有多少是Linq到SQL的开销,以及有多少是查询本身 您可以做很多事情:
CREATE VIEW [SalesCycle_Summary] WITH SCHEMABINDING AS
SELECT AccountManagerID, Status, SUM(ActualAmount) AS MONETARY
,COUNT_BIG(Status) as Counts
FROM [DBO].Opportunities
GROUP BY AccountManagerID, Status
GO
-- Create clustered index on the view; making it an indexed view
CREATE UNIQUE CLUSTERED INDEX IDX_SalesCycle_Summary ON [SalesCycle_Summary] (AccountManagerId);
var allOpps = ctx.slx_Opportunities.Where(opp => opp.STATUS == "open").GroupBy(opp => opp.SALESCYCLE).ToList();
var users = ctx.tbl_Bios.Where(bio => bio.SLXUID != null).ToList().Select(bio => new UserStats
{
LeadNum = allOpps.Single(group => group.Key == "Lead").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Count(),
LeadMoney = allOpps.Single(group => group.Key == "Lead").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Sum(opp => opp.SALESPOTENTIAL.GetValueOrDefault(_default)),
GoingAheadNum = allOpps.Single(group => group.Key == "Going Ahead").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Count(),
GoingAheadMoney = allOpps.Single(group => group.Key == "Going Ahead").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Sum(opp => opp.SALESPOTENTIAL.GetValueOrDefault(_default)),
EnquiryNum = allOpps.Single(group => group.Key == "Enquiry").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Count(),
EnquiryMoney = allOpps.Single(group => group.Key == "Enquiry").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Sum(opp => opp.SALESPOTENTIAL.GetValueOrDefault(_default)),
GoodPotentialNum = allOpps.Single(group => group.Key == "Good Potential").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Count(),
GoodPotentialMoney = allOpps.Single(group => group.Key == "Good Potential").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Sum(opp => opp.SALESPOTENTIAL.GetValueOrDefault(_default)),
PriceOnlyNum = allOpps.Single(group => group.Key == "Price Only").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Count(),
PriceOnlyMoney = allOpps.Single(group => group.Key == "Price Only").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Sum(opp => opp.SALESPOTENTIAL.GetValueOrDefault(_default)),
ProvisionalNum = allOpps.Single(group => group.Key == "Provisional Booking").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Count(),
ProvisionalMoney = allOpps.Single(group => group.Key == "Provisional Booking").Where(opp => opp.ACCOUNTMANAGERID == bio.SLXUID).Sum(opp => opp.SALESPOTENTIAL.GetValueOrDefault(_default)),
Name = bio.FirstName + " " + bio.SurName
}).ToList();