2017-03-02 10 views
3

私の難問は、SQLクエリに近い同等(パフォーマンス賢明)LINQに次のT-SQLクエリを変換しようとしている:このLINQのより洗練されたバージョン

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), 
} 

は(LINQPadを使用して)LINQクエリから生成されたSQLです:

-- 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] 

目立つ点の1つは、LINQクエリから生成されたSQLは、サブクエリで返された各列の集計を実行しますが、元は外部SELECTの一部です。パフォーマンスの低下の一部がそこにあると私は想像することができます。

LINQ APIでDataContext.ExecuteQuery()メソッドを使用するだけで、最初のSQL文を直接実行して整形することができます。しかし、私が現在取り組んでいるプロジェクトにはできるだけ多くの埋め込みSQLを含まないようにしています。元のクエリのパフォーマンスに近づけることができれば理想的です。私はこれをしばらくの間ハッキングしています(学問的な演習として、また実際にこのようなクエリを使用することもあります)。これは私が思いついた最高のものです(元のクエリを書きませんでしたBTW - それは新しいプロジェクトに移行されている古いプロジェクトの一部です)。

ありがとうございます。コメント欄で議論を1として

+0

両方の実行計画を表示できますか? –

+0

そしてあなたのクラスの定義はあまりにも... –

+1

statにはインデックスがありますか?私はそれがユニコード変換だと思う。 –

答えて

1

問題は、LINQツーエンティティがいくつかの未知の理由から追加UNICODEの変換です。 DBは(不要な)変換のためにインデックスを使用できません。

==の代わりに.Equalsを使用できます。UNICODEを使用しないでください。または、dbをvarchar(1)に変更してください。

関連する問題