LinqToEntities产生不正确的SQL(Doubled子查询)

Kye*_*ica 6 c# sql linq linq-to-entities

我有一个LinqToEntities查询,在创建SQL时双重生成子查询.每次运行查询时,这会导致结果集返回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
}
Run Code Online (Sandbox Code Playgroud)

生成这个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)
Run Code Online (Sandbox Code Playgroud)

编辑:

所以将子查询更改为有效,但我不明白为什么

(from jp2 in JobProviderJobs 
    where jp2.JobID == 4725 
    orderby Guid.NewGuid() 
    select jp2).FirstOrDefault().JobProviderID
Run Code Online (Sandbox Code Playgroud)

Str*_*ior 3

这样做是由于 的预期行为FirstOrDefault()。调用FirstOrDefault()空的 JobProviderJobs 集会产生一个null值,但调用空的ints 集会产生一个0. 认识到这一点,LINQ to Entities 尝试在查询末尾调用 case 语句,以确保如果没有匹配的 JobProviderJobs,则选择结果将为 0 而不是 null。

在大多数情况下,重新创建内部投影不会造成任何问题,但您的使用NewGuid()显然会抛出这种逻辑。

您找到了一种解决方案。另一种方法是这样转换内部表达式的结果:

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
}
Run Code Online (Sandbox Code Playgroud)

更正确的实现是重用初始 SQL 投影,而不是创建两个等效的投影。解析器很可能不够复杂,无法正确处理这个问题,并且开发人员从未认为有必要修复它,因为只要相同的投影是确定性的,SQL Server 就应该能够优化它们。

如果尚不存在,您可能应该记录有关此问题的错误报告。