我的难题是尝试将以下T-SQL查询转换为近似等效(性能明智)的LINQ to SQL查询:
SELECT
j1.JOB,
max(CASE WHEN ISNULL(logs.statcategory, ' ') = 'PREP' THEN 'X' ELSE ' ' END) AS prep,
max(CASE WHEN ISNULL(logs.statcategory, ' ') = 'PRINT' THEN 'X' ELSE ' ' END) AS press,
max(CASE WHEN ISNULL(logs.statcategory, ' ') = 'BIND' THEN 'X' ELSE ' ' END) AS bind,
max(CASE WHEN ISNULL(logs.statcategory, ' ') = 'SHIP' THEN 'X' ELSE ' ' END) AS ship
from
job j1
left outer join
(
select
j.job,
l.statcategory,
cnt=count(*)
from
job j
join
jobloc jl
join location l
on
l.code = jl.location and
l.site = jl.site
on j.job = jl.job
WHERE
j.stat = 'O'
group by
j.job,l.statcategory
) logs
on
j1.job = logs.job
WHERE
j1.stat = 'O'
group by
j1.job
此查询目前在MS SQL Server上运行不到0.2秒。我提出的以下LINQ查询返回完全相同的记录,但运行速度慢了近30倍:
from a0 in Jobs
join a1 in
(
from a0 in Jobs
join a1 in JobLocs on a0.Content equals a1.Job
join a2 in Locations on new {Code = a1.Location, a1.Site} equals new {a2.Code, a2.Site}
where a0.Stat == 'O'
select new {a0.Content, a2.StatCategory}
) on a0.Content equals a1.Content into a1
from a2 in a1.DefaultIfEmpty()
where a0.Stat == 'O'
group a2 by a0.Content into a0
orderby a0.Key
select new
{
Job = a0.Key,
Prep = (bool?)a0.Max(a1 => a1.StatCategory == "PREP" ? true : false),
Print = (bool?)a0.Max(a1 => a1.StatCategory == "PRINT" ? true : false),
BIND = (bool?)a0.Max(a1 => a1.StatCategory == "BIND" ? true : false),
SHIP = (bool?)a0.Max(a1 => a1.StatCategory == "SHIP" ? true : false),
}
以下是LINQ查询生成的SQL(使用LINQPad):
-- Region Parameters
DECLARE @p0 Int = 79
DECLARE @p1 Int = 79
DECLARE @p2 VarChar(1000) = 'PREP'
DECLARE @p3 VarChar(1000) = 'PRINT'
DECLARE @p4 VarChar(1000) = 'BIND'
DECLARE @p5 VarChar(1000) = 'SHIP'
-- EndRegion
SELECT [t4].[Job], [t4].[value] AS [Prep], [t4].[value2] AS [Print], [t4].[value3] AS [BIND], [t4].[value4] AS [SHIP]
FROM (
SELECT MAX(
(CASE
WHEN [t3].[StatCategory] = @p2 THEN 1
WHEN NOT ([t3].[StatCategory] = @p2) THEN 0
ELSE NULL
END)) AS [value], MAX(
(CASE
WHEN [t3].[StatCategory] = @p3 THEN 1
WHEN NOT ([t3].[StatCategory] = @p3) THEN 0
ELSE NULL
END)) AS [value2], MAX(
(CASE
WHEN [t3].[StatCategory] = @p4 THEN 1
WHEN NOT ([t3].[StatCategory] = @p4) THEN 0
ELSE NULL
END)) AS [value3], MAX(
(CASE
WHEN [t3].[StatCategory] = @p5 THEN 1
WHEN NOT ([t3].[StatCategory] = @p5) THEN 0
ELSE NULL
END)) AS [value4], [t0].[Job]
FROM [Job] AS [t0]
LEFT OUTER JOIN ([Job] AS [t1]
INNER JOIN [JobLoc] AS [t2] ON [t1].[Job] = [t2].[Job]
INNER JOIN [Location] AS [t3] ON ([t2].[Location] = [t3].[Code]) AND ([t2].[Site] = [t3].[Site])) ON ([t0].[Job] = [t1].[Job]) AND (UNICODE([t1].[Stat]) = @p0)
WHERE UNICODE([t0].[Stat]) = @p1
GROUP BY [t0].[Job]
) AS [t4]
ORDER BY [t4].[Job]
突出的一点是,LINQ查询生成的SQL运行子查询中返回的每个列的聚合,而在原始中它是外部SELECT的一部分。我可以想象部分性能下降是存在的。
我(暂时)愿意接受没有更好的方法来编写它,只需在LINQ API中使用DataContext.ExecuteQuery()
方法(并直接运行和整形第一个SQL语句)。但是,我正在尝试不在我正在处理的项目中尽可能多地包含嵌入式SQL,因此如果它可以接近原始查询的性能,那将是理想的。我一直在讨论这段时间(部分是作为学术练习,也是实际使用这个或类似的查询),这是我提出的最好的(我没有写出原始查询顺便说一句 - 它是旧项目的一部分,正在迁移到更新的项目。)
感谢您的帮助。
答案 0 :(得分:1)
根据我们在评论中的讨论,
问题在于linq-to-entities从某种未知原因添加的UNICODE
转换。
由于(不必要的)转换,DB无法使用索引。
您可以使用.Equals
代替==
,但不会使用UNICODE
或将类型更改为数据库中的varchar(1)
。