Ø 简介
在平常的开发中,我们经常会编写各种各样的 SQL 语句,比如:SQL 查询、存储过程、或者视图查询等。当我们编写的 SQL 语句比较复杂,或者表的数据量比较大,导致查询超时!这时,就要去分析我们的 SQL 语句,导致耗时较长的原因,从而优化我们的 SQL 语句。
说明:本文仅为笔者所思、所想、所写,有用之处欢迎借鉴,不对之处欢迎指出。
1. 内连接查询中,子查询(关联相同的两张表)使用 TOP 子句解决耗时
1) LINQ 语句
var datas = (from t1 in DataContext.Orders
join t3 in DataContext.UserInfoes on t1.UserId equals t3.id
join t5 in DataContext.Customers on t3.CustomerId equals t5.Id
join t7 in (from t1 in DataContext.CateringCategorys
join t3 in DataContext.CateringCategorys on t1.CategoryId equals t3.ParentId
where t1.Level == 1
select new
{
CategoryId1 = t1.CategoryId,
CategoryId2 = t3.CategoryId
}) on t5.TradeType equals t7.CategoryId2
where 1 == 1
&& (cityId == 0 || t1.CityId == strCityId)
&& t1.OrderStatusId >= (int)OrderStates.Undelivered && t1.OrderStatusId < (int)OrderStates.Cancelled
&& t1.PayStatusId != (int)OrderPayStates.Unpaid
&& (t1.PayTime >= startDate && t1.PayTime < endDate)
select new
{
CategoryId = t7.CategoryId1,
OrderId = t1.Id,
t1.RealTotal,
t3.CustomerId
}).ToArray();
2) 生成 SQL(sp_executesql 转换后的同等 SQL 语句)
SELECT
[Filter2].[CategoryId1] AS [CategoryId],
[Filter1].[Id1] AS [Id],
[Filter1].[RealTotal] AS [RealTotal],
[Filter1].[CustomerId] AS [CustomerId]
FROM (SELECT [Extent1].[Id] AS [Id1], [Extent1].[RealTotal] AS [RealTotal], [Extent1].[PayTime] AS [PayTime], [Extent1].[CityId] AS [CityId1], [Extent2].[CustomerId] AS [CustomerId], [Extent3].[TradeType] AS [TradeType]
FROM [dbo].[Orders] AS [Extent1]
INNER JOIN [dbo].[UserInfo] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[id]
INNER JOIN [dbo].[Customer] AS [Extent3] ON [Extent2].[CustomerId] = [Extent3].[Id]
WHERE ([Extent1].[OrderStatusId] >= 2) AND ([Extent1].[OrderStatusId] < 10) AND (cast(1 as bigint) <> [Extent1].[PayStatusId]) ) AS [Filter1]
INNER JOIN (SELECT [Extent4].[CategoryId] AS [CategoryId1], [Extent5].[CategoryId] AS [CategoryId2]
FROM [dbo].[Crm_CateringCategory] AS [Extent4]
INNER JOIN [dbo].[Crm_CateringCategory] AS [Extent5] ON [Extent4].[CategoryId] = [Extent5].[ParentId]
WHERE 1 = [Extent4].[Level] ) AS [Filter2] ON [Filter1].[TradeType] = [Filter2].[CategoryId2]
WHERE ((0 = 0) OR ([Filter1].[CityId1] = '0') OR (([Filter1].[CityId1] IS NULL) AND ('0' IS NULL))) AND ([Filter1].[PayTime] >= '2018-12-01 00:00:00') AND ([Filter1].[PayTime] < '2019-01-01 00:00:00')
3) 执行结果
执行以上语句,耗时为:00:01:43.853
4) SQL 分析
1. 首先,我们创建了一个子查询([Filter1]),关联了三张表:Orders、UserInofo 和 Customer,这没什么好说的,是一个正常查询。
2. 另外,又关联了一个子查询([Filter2]),Crm_CateringCategory 与 Crm_CateringCategory 关联(使用 CategoryId 和 ParentId 关联),就是因为这个子查询,导致了较长的耗时!这是为什么呢,这里先打个问号?
3. 然后,笔者开始各种猜测
1) 这个子查询数量量大?NO,只有23记录。
2) 在关联的第二个 Crm_CateringCategory 表上再派生一层,再关联外层 Crm_CateringCategory 表,并只输出所需字段?结果还是不行!
3) 因为关联的是相同的表?对的,就是这个原因!因为,尝试创建与 CateringCategory 相同的另一张表 CateringCategory_1 去关联查询,耗时问题就解决了。
4) 为什么呢,表上加 (NOLOCK) 关键字也不管用,原因不祥(如有博友们知道,欢迎指出)!
4. 然后,笔者又尝试在这个子查询上加上 TOP 子句,结果执行耗时变为了 00:00:00.740,耗时问题同样解决了。因为开发中,不可能再去创建一张相同的一张表。(Linq 只需加上 Take() 方法即可)。