ЧРЕЗВЫЧАЙНО Плохое представление запросов LINQ при использовании Skip/Take for Paging

81
10

Мне нужно запросить записи из базы данных DB2 с помощью LINQ. У меня есть сущности, которые были созданы из схемы БД, и я пытаюсь выполнить запрос LINQ, используя Skip и Take. Базовая таблица имеет 25 столбцов и, возможно, миллион записей. Когда я выполняю запрос без "Skip()", для завершения требуется примерно 508 миллисекунд. Когда я включаю Skip(), он занимает около 30 секунд. Большая разница.


Может ли кто-нибудь сказать мне, почему это происходит?


ОБНОВЛЕНИЕ: Вот запрос LINQ, который я использую.


var x = 30;

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


UPDATE: Поэтому я просто попробовал обновить запрос, чтобы я возвращал только один столбец ASSET_ID. Когда я возвращаю только один столбец, запрос WITH 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();


UPDATE: Я обнаружил, что есть проблемы (возможно, связанные с индексами) с столбцами в таблице, которую я пытаюсь выполнить. Как я уже упоминал выше, когда я выполняю запрос, который возвращает только столбец ASSET_ID, он принимает только 0,256 миллисекунды. Если я попытаюсь выполнить запрос, в котором ТОЛЬКО возвращает ASSET_DESC или запрос, который ТОЛЬКО возвращает ASSET_TYP, время выполнения запроса скачкообразно приближается к 9 секундам.


Означает ли это, что эти другие столбцы в настоящее время не индексируются?


UPDATE: Я добавил вывод SQL из вышеуказанного запроса LINQ.


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

спросил(а) 2013-11-13T17:29:00+04:00 6 лет, 10 месяцев назад
1
Решение
58

Я предполагаю, что это не из-за linq, сначала попробуйте упорядочить индексированный столбец, а затем пропустите.
В первом случае, когда вы просто берете, он не проходит через остальную часть записей и просто выбирает верхний результат, поэтому пропустите его, чтобы отсортировать их все, чтобы определить порядок, а затем пропустить на нем.

ответил(а) 2013-11-13T17:40:00+04:00 6 лет, 10 месяцев назад
70

Вы посмотрели на 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-40 м.


Это означало бы, что вам придется подумать о том, как вы код для достижения пейджинга, и не может быть практически осуществимым для реализации в вашем случае.

ответил(а) 2013-11-13T20:32:00+04:00 6 лет, 10 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема