【问题标题】:LinqToEntities produces incorrect SQL (Doubled Subquery)LinqToEntities 产生不正确的 SQL(双重子查询)
【发布时间】:2012-06-29 22:10:49
【问题描述】:

我有一个 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
}

生成此 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

【问题讨论】:

    标签: c# sql linq linq-to-entities


    【解决方案1】:

    这样做是因为FirstOrDefault() 的预期行为。在一个空的 JobProviderJobs 集上调用 FirstOrDefault() 会产生一个 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
    }
    

    更正确的实现将重用初始 SQL 投影,而不是创建两个等效的投影。很可能解析器根本不够复杂,无法正确处理这个问题,开发人员从来没有看到有任何需要修复它,因为 SQL Server 应该能够优化相同的投影,只要它们是确定性的。

    如果不存在,您可能应该记录一份关于此的错误报告。

    【讨论】:

    • 希望我能加 3。完美答案:解释行为、解决方案和“因此”一词的使用 =)
    • @Tyrsius:我在Visual Studio and .NET 下有success 记录错误
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-06
    • 2018-09-07
    • 2017-09-17
    • 2014-03-03
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    相关资源
    最近更新 更多