6

SQL の作成時に二重にサブクエリを生成する LinqToEntities クエリがあります。これにより、クエリが実行されるたびに結果セットに 0 ~ 3 個の結果が返されます。サブクエリ自体は、単一のランダムな結果を生成します (そうあるべきです)。ここで何が起こっているのですか?

LINQ クエリ:

from jpj in JobProviderJobs
where jpj.JobID == 4725 
&& jpj.JobProviderID == (from jp2 in JobProviderJobs 
                        where jp2.JobID == 4725 
                        orderby Guid.NewGuid() 
                        select jp2.JobProviderID).FirstOrDefault()
select new 
{
    JobProviderID = jpj.JobProviderID
}

次の SQL を生成します。

SELECT 
[Filter2].[JobID] AS [JobID], 
[Filter2].[JobProviderID1] AS [JobProviderID]
FROM   (SELECT [Extent1].[JobID] AS [JobID], [Extent1].[JobProviderID] AS [JobProviderID1], [Limit1].[JobProviderID] AS [JobProviderID2]
    FROM  [dbo].[JobProviderJob] AS [Extent1]
    LEFT OUTER JOIN  (SELECT TOP (1) [Project1].[JobProviderID] AS [JobProviderID]
        FROM ( SELECT 
            NEWID() AS [C1], 
            [Extent2].[JobProviderID] AS [JobProviderID]
            FROM [dbo].[JobProviderJob] AS [Extent2]
            WHERE 4725 = [Extent2].[JobID]
        )  AS [Project1]
        ORDER BY [Project1].[C1] ASC ) AS [Limit1] ON 1 = 1
    WHERE 4725 = [Extent1].[JobID] ) AS [Filter2]
LEFT OUTER JOIN  (SELECT TOP (1) [Project2].[JobProviderID] AS [JobProviderID]
    FROM ( SELECT 
        NEWID() AS [C1], 
        [Extent3].[JobProviderID] AS [JobProviderID]
        FROM [dbo].[JobProviderJob] AS [Extent3]
        WHERE 4725 = [Extent3].[JobID]
    )  AS [Project2]
    ORDER BY [Project2].[C1] ASC ) AS [Limit2] ON 1 = 1
WHERE [Filter2].[JobProviderID1] = (CASE WHEN ([Filter2].[JobProviderID2] IS NULL) THEN 0 ELSE [Limit2].[JobProviderID] END)

編集:

したがって、サブクエリをこれに変更するとうまくいきますが、その理由はわかりません

(from jp2 in JobProviderJobs 
    where jp2.JobID == 4725 
    orderby Guid.NewGuid() 
    select jp2).FirstOrDefault().JobProviderID
4

1 に答える 1

3

の予想される動作のためにこれを行っていFirstOrDefault()ます。JobProviderJobsFirstOrDefault()の空のセットをnull呼び出すと値が生成されますが、 ints の空のセットを呼び出すと0. これを認識すると、LINQ to Entities はクエリの最後で case ステートメントを呼び出して、一致する JobProviderJobs がない場合、select の結果が null ではなく 0 になるようにします。

ほとんどの場合、内側のプロジェクションを再作成しても問題は発生しませんが、 を使用すると、NewGuid()明らかにこのロジックが無効になります。

1 つの解決策が見つかりました。もう 1 つは、内部式の結果を次のようにキャストすることです。

from jpj in JobProviderJobs
where jpj.JobID == 4725 
&& jpj.JobProviderID == (from jp2 in JobProviderJobs 
                        where jp2.JobID == 4725 
                        orderby Guid.NewGuid() 
                        select (int?) jp2.JobProviderID).FirstOrDefault()
select new 
{
    JobProviderID = jpj.JobProviderID
}

より正確な実装では、2 つの同等のプロジェクションを作成するのではなく、最初の SQL プロジェクションを再利用します。パーサーは、これを適切に処理するほど複雑ではない可能性が高く、SQL Server は決定論的である限り、同一のプロジェクションを最適化して除去できるはずであるため、開発者はそれを修正する必要をまったく感じませんでした。

これに関するバグ レポートがまだ存在しない場合は、おそらくログに記録する必要があります。

于 2012-06-29T22:32:25.023 に答える