Эффективный способ запроса каждого элемента списка
Мне нужно выполнить итерацию по набору объектов (пусть это ID) и выполнить конкретный запрос для каждого из этих объектов. Например:
IEnumerable<int> ids = getIDs(); //[1,2,3,4...]
Сейчас у меня есть это решение:
DBEntities db = new DBEntities();
var results =
from a in db.TABLEA
join b in db.TABLEB on a.id equals b.id
join c in db.TABLEC on b.oid equals c.oid
where ids.Contains(c.id)
select a;
но имейте в виду, что список идентификаторов меньше, чем таблица, в которой я ищу. При этом решение выше кажется неэффективным, так как я ищу каждую запись таблицы против меньшего списка, когда мне хотелось наоборот. Я также не хочу перебирать список и выполнять запрос для одного элемента за раз.
В идеале я хотел бы что-то вроде этого:
DBEntities db = new DBEntities();
(some data structure) ids = getIDs();
var results =
from a in db.TABLEA
join b in db.TABLEB on a.id equals b.id
join c in db.TABLEC on b.oid equals c.oid
join i in ids on c.id equals i.id;
В приведенном выше коде (псевдо) код будет перебирать мои элементы списка в одном запросе, делая это в одном запросе и выполняя мой фильтр каждым элементом списка.
Это способ сделать это? Если да, то какова соответствующая структура данных для реализации этого решения? Если нет, какие альтернативы у меня есть?
Ответы
Ответ 1
Если это linq2Sql (или Linq2Entites), ваш единственный вариант, как в вашем примере 1. Вы не можете "присоединиться" к таблице со списком в памяти. Вы должны использовать Contains
. Который будет переведен в запрос Where c.id IN(2,3,4,5,...)
SQL
Ответ 2
Магнус отвечает верно, но не прав:)
Технически у вас есть два варианта в новых версиях Entity Framework (и я обнаружил это случайно). Contains
, но также и Join
.
Соединение с локальной последовательностью примитивных типов всегда было возможно, но очень быстро (после нескольких десятков элементов) поднял a SqlException
:
Некоторая часть вашего оператора SQL вложен слишком глубоко. Перепишите запрос или разделите его на более мелкие запросы.
EF пытается перевести локальный список во временную таблицу в SQL. Это на удивление нетривиально. Он должен построить таблицу с помощью UNION
-выбора операторов выбора, которые возвращают по 1 элемент каждый. Это то, к чему он привык, только с 5 элементами!
....
INNER JOIN (SELECT
[UnionAll3].[C1] AS [C1]
FROM (SELECT
[UnionAll2].[C1] AS [C1]
FROM (SELECT
[UnionAll1].[C1] AS [C1]
FROM (SELECT
1 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
UNION ALL
SELECT
2 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable2]) AS [UnionAll1]
UNION ALL
SELECT
3 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable3]) AS [UnionAll2]
UNION ALL
SELECT
4 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable4]) AS [UnionAll3]
UNION ALL
SELECT
5 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable5]) AS [UnionAll4] ON ....
Как вы видите, если вы посмотрите внимательно, операторы UNION
вложены. Уровень гнездования вскоре становится слишком глубоким, что делает этот подход практически бесполезным.
Однако в настоящее время SQL выглядит так:
....
INNER JOIN (SELECT
1 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
UNION ALL
SELECT
2 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable2]
UNION ALL
SELECT
3 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable3]
UNION ALL
SELECT
4 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable4]
UNION ALL
SELECT
5 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable5]) AS [UnionAll4] ON ....
Все еще не красота, но вложенность заменяется цепочкой, и список может содержать сотни элементов.
Но... (и именно поэтому ответ Магнуса верен), он не работает хорошо. Простой тест с 2000 элементами в списке занял 2,5 с Join
и .25s с Contains
. Таким образом, нет практического случая для соединения с локальной последовательностью.