麻烦一些linq2sql生成的T-SQL

时间:2013-11-08 15:42:01

标签: c# sql-server tsql linq-to-sql sql-execution-plan

我遇到以下LINQ2SQL查询的SQL超时问题:

DateTime date = DateTime.Parse("2013-08-01 00:00:00.000");

Clients.Where(e => 
    (
        !Orders.Any(f => f.ClientId.Equals(e.Id) && f.OrderDate >= date)
        ||
        Comments.Any(f => f.KeyId.Equals(e.Id))
    )
).Count().Dump();

在LinqPad中运行时,它将需要永远完成,如果在服务器上运行,将成为SQL超时。

生成的SQL代码:

-- Region Parameters
DECLARE @p0 DateTime = '2013-08-01 00:00:00.000'
-- EndRegion
SELECT COUNT(*) AS [value]
FROM [Clients] AS [t0]
WHERE (NOT (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM [Orders] AS [t1]
    WHERE ([t1].[ClientId] = [t0].[Id]) AND ([t1].[OrderDate] >= @p0)
    ))) OR (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM [Comments] AS [t2]
    WHERE [t2].[KeyId] = [t0].[Id]
    ))

在SQL-studio中正常工作!

可是:

SELECT COUNT(*) AS [value]
FROM [Clients] AS [t0]
WHERE 

(NOT (EXISTS(SELECT NULL AS [EMPTY] FROM [Orders] AS [t1] WHERE ([t1].[ClientId] = [t0].[Id]) AND ([t1].[OrderDate] >= '2013-08-01 00:00:00.000')))) 

OR  

(EXISTS(SELECT NULL AS [EMPTY] FROM [Comments] AS [t2] WHERE [t2].[KeyId] = [t0].[Id]))

并且会在LinqPad中实际运行查询时遇到问题。

与使用常量日期相比,使用DECLARE @p0 DateTime = '2013-08-01 00:00:00.000'有什么不同?如何让Linq2SQL工作?

编辑:

查看两个查询的执行计划:

超时: TIMEOUTS

精细: FINE

我注意到的其他一些事情是,如果我删除NOT,它可以正常工作:

SELECT COUNT(*) AS [value]
FROM [Clients] AS [t0]
WHERE 

((EXISTS(SELECT NULL AS [EMPTY] FROM [Orders] AS [t1] WHERE ([t1].[ClientId] = [t0].[Id]) AND ([t1].[OrderDate] >= '2013-08-01 00:00:00.000')))) 

OR  

(EXISTS(SELECT NULL AS [EMPTY] FROM [Comments] AS [t2] WHERE [t2].[KeyId] = [t0].[Id]))

或者,如果我删除OR EXISTS部分,它也可以正常工作:

SELECT COUNT(*) AS [value]
FROM [Clients] AS [t0]
WHERE 

((EXISTS(SELECT NULL AS [EMPTY] FROM [Orders] AS [t1] WHERE ([t1].[ClientId] = [t0].[Id]) AND ([t1].[OrderDate] >= '2013-08-01 00:00:00.000')))) 

由于 /尼尔斯

3 个答案:

答案 0 :(得分:5)

您的订单表必须相当大。您有OrderDate的索引吗? 在此示例中,SQL Server实际上生成了2个不同的执行计划。或者,如果它生成相同的计划,则SQL为2个语句提供了大大不同的返回行数。

DECLARE @p0 DateTime = '2013-08-01 00:00:00.000'
SELECT * FROM Orders WHERE OrderDate >= @p0
SELECT * FROM Orders WHERE OrderDate >= '2013-08-01 00:00:00.000'

第一个语句生成参数化查询,计划优化器将假设@ p0当时未知,并选择最适合未知值的执行计划。 第二个语句,优化器将考虑您提供的固定值。 SQL将查看索引分布并估计将按> ='2013-08-01'

过滤的行数

答案 1 :(得分:3)

执行计划不可见,但一般来说,sql性能建议不要使用否定它总是会影响性能。在您的情况下,尝试使用< =而不是使用> =

如果你使用很多,它也会影响你的表现。尝试使用subquerys作为一种解决方法,不要使用很多或否定。

答案 2 :(得分:2)

我的解决方案是重建OrderDate的索引。