使用Skip / Take进行分页时,LINQ查询性能极差

时间:2013-11-13 14:29:28

标签: c# linq linq-to-entities paging query-performance

我需要使用LINQ从DB2数据库查询记录。我有从DB模式生成的实体,并尝试使用Skip和Take执行LINQ查询。基础表有25列,可能有100万条记录。当我执行没有“Skip()”的查询时,完成大约需要.508毫秒。当我包含Skip()时,它需要接近30秒。差异很大。

谁能告诉我为什么会这样?

UPDATE:这是我正在使用的LINQ查询。

var x = 30;

var results = context.ASSET_T
.OrderBy(c => c.ASSET_ID)
.Skip(x)
.Take(x)
.ToList();

更新:所以我只是尝试更新查询,这样我只返回一个列ASSET_ID。当我只返回那一列时,带有Skip()的查询只需要.256毫秒。

var x = 30;

var results = context.ASSET_T
.OrderBy(c => c.ASSET_ID)
.Skip(x)
.Take(x)
.Select(c => c.ASSET_ID)
.ToList();

如果我包含任何其他列,则查询执行时间会增加 DRAMATICALLY

例如,下面的查询需要10秒才能执行。

var x = 30;

var results = context.ASSET_T
.OrderBy(c => c.ASSET_ID)
.Skip(x)
.Take(x)
.Select(c => new {
                 ASSET_ID = c.ASSET_ID,
                 ASSET_TYP = c.ASSET_TYP
                 ASSET_DESC = c.ASSET_DESC
                 })
.ToList();

更新:我现在发现我正在尝试查询的表中的列存在问题(可能与索引相关)。正如我上面提到的,当我执行只返回ASSET_ID列的查询时,它只需要.256毫秒。如果我尝试执行 返回ASSET_DESC的查询或 返回ASSET_TYP的查询,则查询执行时间跳到9秒左右。

这是否表示当前没有对其他列进行索引?

UPDATE:我已经从上面的LINQ查询中添加了SQL输出。

SELECT 
Project1.C1 AS C1, 
Project1.ASSET_ID AS ASSET_ID, 
Project1.ASSET_TYP AS ASSET_TYP, 
Project1.ASSET_DESC AS ASSET_DESC
FROM ( SELECT Project1.ASSET_ID AS ASSET_ID, Project1.ASSET_TYP AS ASSET_TYP, Project1.ASSET_DESC AS ASSET_DESC, Project1.C1 AS C1, row_number() OVER (ORDER BY Project1.ASSET_ID ASC, Project1.ASSET_TYP ASC, Project1.ASSET_DESC ASC) AS row_number
  FROM ( SELECT 
    Extent1.ASSET_ID AS ASSET_ID, 
    Extent1.ASSET_TYP AS ASSET_TYP, 
    Extent1.ASSET_DESC AS ASSET_DESC, 
    CAST(1 AS int) AS C1
    FROM MYDB.ASSET_T AS Extent1
  )  AS Project1
)  AS Project1
WHERE Project1.row_number > 1
ORDER BY Project1.ASSET_ID ASC, Project1.ASSET_TYP ASC, Project1.ASSET_DESC ASC FETCH  FIRST 31 ROWS ONLY 

2 个答案:

答案 0 :(得分:2)

您是否查看了为此查询生成的SQL?

据我所知Skip()Take()最终会生成一个使用名为Row_Number()的函数的生成语句。此函数以下面显示的方式在整个记录集中执行 - 在您希望的开始值和结束值之间取值之前,将行号插入结果中的第一个生成列,通常使其非常慢,在大型记录上套..

SELECT ...
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY [t0].[...]) AS [ROW_NUMBER], ... ,
    FROM [table] AS [t0]
    ) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
ORDER BY [t1].[ROW_NUMBER]

如果您可以使用索引数字列并对其进行排列,以便您自己读取> = start_value AND< = end-value,那么将这些值向上移动您的分页量,它将使用索引并返回结果毫秒。

我拥有完整的索引数据库,拥有100百万条记录和Skip()。Take()最多可能需要30分钟才能获得25条记录。直接读取在哪里需要大约20-40ms。

这意味着您必须考虑编码方式以实现分页,并且在您的情况下可能无法实现。

答案 1 :(得分:1)

我猜这不是由于linq,首先尝试按索引列排序,然后跳过。 在你的第一种情况下,当你刚刚接受时,它不会记录其余的记录而只是选择最高结果,跳过它需要对它们进行排序以找出顺序,然后根据它进行跳过。