Вчера хороший человек помог мне построить PredicateBuilder
для Linq to Entities
here.
Кажется, что все в порядке, однако полный запрос порождает эту отвратительную вещь длиной 70 000 строк here (слишком длинная для вставки) и повышение SQL statement is nested too deeply
.Почему этот запрос поднимает «заявление SQL слишком глубоко вложен»?
Вот контекст:
Пользователь ищет список животных, соответствующих его критериям, в частности, в отношении способностей.
В GUI для каждого типа способностей (например: «управляемость», «маневренность» и т. Д.) Пользователь может выбрать модификатор («>», «<» или «=») и значение.
Например, он может хотеть, чтобы отобразить «Все животные, которые имеют потенциальную способность> 3 в маневренности», или «Все животные, которые обладают способностью умение < 10 в maniability и способности потенциального = 2 в маневренности»
О базе:
Player
с колоннами Id
Animal
с колоннами Id
Ability
с колоннами:
Id
AnimalId
TypeId
(представляющий Enum, который может быть "Потенциал", "BirthPotentiel" или "навык")AbilityId
(представляющий Enum, который может быть "ловкость", или "Maniability")Value
Таким образом, каждое животное имеет свойство AllAbilities
которое является ICollection<Ability>
.
Это функция поиска (все параметры были ранее введены или оставлены пустыми пользователем в графическом интерфейсе).
public async Task<List<Animal>> Search
(
Player player,
int speciesId,
int breedId,
int coatId,
int genderId,
int minAge,
int maxAge,
int priceModifier, // int representing an Enum Criteria.ModifierE: ">", "<" or "="
int priceValue,
string ownerPseudo,
bool isSearchingOwn,
int minHeight,
int maxHeight,
int minWeight,
int maxWeight,
List<int> character, // representing list of Enum Flags
List<int> abilitySkillModifiers, // representing list of Enum ModifierE: ">", "<" or "="
List<int> abilitySkillValues,
List<int> abilityPotentialModifiers, // representing list of Enum ModifierE: ">", "<" or "="
List<int> abilityPotentialValues
)
{
// You can see "PredicateUtils" class following the first link of this post
var filter = PredicateUtils.Null<Animal>();
filter = filter.And(e => speciesId != -1 ? e.SpeciesId == speciesId : true);
filter = filter.And(e => breedId != -1 ? e.BreedId == breedId : true);
filter = filter.And(e => coatId != -1 ? e.CoatId == coatId : true);
filter = filter.And(e => genderId != -1 ? e.GenderId == genderId : true);
filter = filter.And(e => minAge != -1 ? e.age >= minAge : true);
filter = filter.And(e => maxAge != -1 ? e.age <= maxAge : true);
string pseudo = isSearchingOwn ? player.Pseudo : ownerPseudo;
filter = filter.And(e => !string.IsNullOrEmpty(ownerPseudo) ? e.Owner.Pseudo.Equals(pseudo, StringComparison.InvariantCultureIgnoreCase) : true);
filter = filter.And(e => minHeight > 0 ? e.FinalHeight >= minHeight : true);
filter = filter.And(e => maxHeight > 0 ? e.FinalHeight <= maxHeight : true);
filter = filter.And(e => minWeight > 0 ? e.FinalWeight >= minWeight : true);
filter = filter.And(e => maxWeight > 0 ? e.FinalWeight <= maxWeight : true);
filter = filter.And(e => character.All(c => (e.character & c) == c));
for (int i = 0; i < abilitySkillValues.Count; i++)
{
filter = filter.And(
AbilitySkillFilter
(
(Criteria.ModifierE)abilitySkillModifiers[i], // ">", "<", or "="
i,
abilitySkillValues[i] // value entered by the user for the current ability
)
);
}
for (int i = 0; i < abilityPotentialValues.Count; i++)
{
filter = filter.And(
AbilityPotentialFilter
(
(Criteria.ModifierE)abilityPotentialModifiers[i], // ">", "<", or "="
i,
abilityPotentialValues[i] // value entered by the user for the current ability
)
);
}
return await GetAll(filter);
}
И функция фильтра способность:
static Expression<Func<Animal, bool>> AbilitySkillFilter(Criteria.ModifierE modifier, int abilityId, int userValue)
{
if (modifier == Criteria.ModifierE.More) // User chose ">"
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId).Value >= userValue
: value <= 0;
else if (modifier == Criteria.ModifierE.Equal) // User chose "<"
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId).Value == userValue
: value == 0;
else if (modifier == Criteria.ModifierE.Less) // User chose "<"
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId).Value <= userValue
: value >= 0;
else
return null;
}
static Expression<Func<Animal, bool>> AbilityPotentialFilter(Criteria.ModifierE modifier, int abilityId, int userValue)
{
if (modifier == Criteria.ModifierE.More)
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId).Value >= userValue
: e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.BirthPotential && a.AbilityId == abilityId).Value >= userValue;
else if (modifier == Criteria.ModifierE.Equal)
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId).Value == userValue
: e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.BirthPotential && a.AbilityId == abilityId).Value == userValue;
else if (modifier == Criteria.ModifierE.Less)
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId).Value <= userValue
: e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.BirthPotential && a.AbilityId == abilityId).Value <= userValue;
else
return null;
}
Объяснение:
В базе данных, Ability
строки с TypeId == Potential
или TypeId == Skill
не может существовать, в то время как TypeId == BirthPotential
всегда делают.
- В случае
TypeId == Potential
не существует для текущего животного и токAbilityId
, я хочу сравнить значение пользователя сTypeId == BirthPotential
значением строки (который всегда существует). - В случае, если
TypeId == Skill
не существует для текущего животного и текущегоAbilityId
, я хочу сравнить значение пользователя с 0.
Если у кого есть какие-либо предположения о том, почему этот запрос производит такой ужасный вывод и имеет улучшение, я был бы очень благодарен. Не стесняйтесь, если вам нужна дополнительная информация.
РЕШЕНИЕ:
Это, наконец, работает, благодаря juharr
предложения (используя простой if
вместо трехкомпонентной if
не добавить пункт, если нет необходимости), в сочетании с Ivan Stoev
раствором.
С критериями по возрасту, полу, видам, псевдо, минимкам, максимуму, характеру, навыкам и одной потенциальной способности, вот новый выход SQL: почти 70 000 строк до 60!
Result here
Большое спасибо!
Является ли SQL Server работает на той же машине, что и приложение? Если это так, возможно, получить все отфильтрованные данные (без фильтров в циклах) в локальную коллекцию, а затем сделать фильтрацию циклов на этом параметре. – schlonzo
Вместо того чтобы помещать if condtion в предикат, почему бы не условно применять их как 'if (speciesId! = -1) filter = filter.And (e => e.SpeciesId == speciesId);'. Это может помочь уменьшить сложность запроса. – juharr
@schlonzo: Да, это так. Поэтому вы бы рекомендовали «ждать GetAll (filter)» дважды, один прямо перед циклами и один в конце? @juharr: Действительно, вы правы, я отредактирую свой код таким образом. Однако, если пользователь вводит все критерии, он все равно не сможет получить результаты: ( –