Я понимаю, что && замыкает оценку, поэтому ему не нужно оценивать RHS, если в этом нет необходимости, но как насчет EF? Есть ли разница между & и && (то же самое для | и ||)? по производительности!





Согласно документации Microsoft:
1. & Оператор поддерживается в двух формах: унарный оператор адреса или бинарный логический оператор..
Унарный адрес оператора:
Унарный оператор & возвращает адрес своего операнда. Для получения дополнительной информации см. Как: получить адрес переменной. Оператор адреса & требует небезопасного контекста.
Целочисленный логический побитовый оператор И:
Для целочисленных типов оператор & вычисляет логические побитовые AND своих операндов:
uint a = 0b_1111_1000;
uint b = 0b_1001_1111;
uint c = a & b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10011000
2. && Оператор — это условный логический оператор И, также известный как «замыкающий» логический оператор И, вычисляющий логическое И своих логических операндов. Результат x && y истинен, если и x, и y оцениваются как true. В противном случае результат false. Если первый операнд оценивается как false, второй операнд не оценивается, а результатом операции является false. Следующий пример демонстрирует такое поведение:
bool SecondOperand()
{
Console.WriteLine("Second operand is evaluated.");
return true;
}
bool a = false && SecondOperand(); // <-- second operand is not evaluated here
Console.WriteLine(a);
// Output:
// False
bool b = true && SecondOperand(); // <-- second operand is evaluated here
Console.WriteLine(b);
// Output:
// Second operand is evaluated.
// True
Теперь, в случае поведения EF/EF Core, @Ivan Stoev подробно объяснил это:Здесь
Спасибо @TanvirArjel, вопрос о поведении EF.
but what about EF? Is there any differences between & and && (same for | and ||)? performance-wise!
Да, есть.
Во-первых, иногда EF Core использует оценка клиента, поэтому его нельзя замкнуть накоротко.
Во-вторых, даже при использовании оценки сервера EF Core интерпретирует их по-разному. Например, вот перевод для SqlServer:
LINQ SQL
============== ============
expr1 && expr2 expr1 AND expr2
expr1 & expr2 (expr1 & expr2) = 1
Влияет ли это на производительность, зависит от оптимизатора запросов к базе данных, но в целом первый выглядит лучше. А некоторые поставщики баз данных, которые не имеют встроенной поддержки типа bool, могут генерировать довольно неэффективный перевод или даже не переводить предикаты & / |.
Короче говоря, желательно всегда использовать логические операторы && и || в запросах LINQ.
Здорово! Но не понял эту строчку so it can't be short-circuited.
@TanvirArjel Я имел в виду, что при выполнении на стороне клиента используются те же правила оценки, что и в соответствующем коде C#. например && и || замкнет накоротко, & и | — нет.
Я понял это! но в случае сервера sql expr1 AND expr2 ведет себя не так, как C# expr1 && expr2? или что-нибудь еще.
@TanvirArjel Часть короткое замыкание имеет значение для оценка клиента. Для оценка сервера разница заключается в переводе SQL, что, скорее всего, повлияет на производительность из-за неестественных WHERE условий.
Мой вопрос: в случае expr1 AND expr2 на SQL-сервере, если первый операнд ложен, будет оцениваться второй операнд или нет?
@TanvirArjel Способ оценки запроса зависит от оптимизаторов запросов к базе данных и создаваемых ими планов выполнения запросов. Им разрешено делать многое - переупорядочивать условия (чтобы использовать индекс) и т. д., поэтому в целом на ваш вопрос нельзя ответить.
Я в одном шаге от того, чтобы проголосовать за ваш ответ :) Другой вопрос: в случае (expr1 & expr2) = 1 на SQL-сервере, какое влияние это окажет на запрос, я не уверен в этом?
@TanvirArjel Опять же, неизвестно, как перевод SQL влияет на план запроса. SqlServer может быть умным и относиться к нему как к AND, а может и нет. И у Oracle, например, определенно будут проблемы как с переводом, так и с выполнением. Дело в том, что AND и OR — это способы естественный представления бинарных логических условий внутри предикатов SQL. Идея SQL состоит в том, чтобы описать желаемый результат, а не как для этого. Что отличается от LINQ to Objects, где выполнение зависит от того, как вы составляете запрос.
Хорошо! Большое спасибо, я буду экспериментировать с этим на уровне SQL-сервера.
Решил провести эксперимент:
var t1 = _db.Customers
.Where(x => x.FirstName == "james" | x.FirstName == "thomas")
.Select(x=>x.CustomerId).ToList();
против.
var t2 = _db.Customers
.Where(x => x.FirstName == "james" || x.FirstName == "thomas")
.Select(x => x.CustomerId).ToList();
t1 был выполнен как:
SELECT [x].[CustomerId]
FROM [Customers] AS [x]
WHERE (CASE
WHEN [x].[FirstName] = N'james'
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END | CASE
WHEN [x].[FirstName] = N'thomas'
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END) = 1
и t2 как:
SELECT [x].[CustomerId]
FROM [Customers] AS [x]
WHERE [x].[FirstName] IN (N'james', N'thomas')
Оба возвращают одинаковый результат. Я сравнил их план выполнения, для выполнения t1 у меня было «Сканирование индекса», а для выполнения t2 у меня был «Поиск индекса». Как упомянул Джастин (Планы SQL Server: разница между сканированием индекса и поиском индекса): «Поиск всегда лучше, чем сканирование, поскольку он более эффективен в том, как он ищет данные».
Итак, используя | или & не только это может привести к оценке на стороне клиента, но также повлияет на сгенерированный sql.
Всем спасибо!
Если вам это нужно для ef (Core или нет), и вам нужны условия, я бы посоветовал всегда использовать && или ||. Потому что побитовые операторы в данный момент вам не знакомы.