Подтвердить что ты не робот

Различные SQL, созданные из Where (l => l.Side == 'A') vs Where (l => l.Side.Equals('A')

Я экспериментировал с запросами в LinqPad. У нас есть таблица Lot со столбцом Side char(1). Когда я пишу запрос linq to sql Lots.Where(l => l.Side == 'A'), он создает следующий SQL

-- Region Parameters
DECLARE @p0 Int = 65
-- EndRegion
SELECT ..., [t0].[Side], ...
FROM [Lot] AS [t0]
WHERE UNICODE([t0].[Side]) = @p0

Однако, используя Lots.Where(l => l.Side.Equals('A')), он производит

-- Region Parameters
DECLARE @p0 Char(1) = 'A'
-- EndRegion
SELECT ..., [t0].[Side], ...
FROM [Lot] AS [t0]
WHERE [t0].[Side] = @p0

Было бы (хотя и наивно) проверено, что последнее будет немного быстрее, так как ему не нужно звонить UNICODE.

Используя столбцы int, smallint или varchar, нет разницы между созданным SQL с == или .Equals, почему char(1) и соответствующий тип С# char отличается?

Есть ли способ предсказать, будет ли заданный тип столбца создавать различный SQL с двумя формами проверки равенства?

Edit:

Я проверил каждый тип, поддерживаемый MS SQL, и только char(1) и nchar(1) показывают это поведение. Оба они представлены в LinqToSql по типу System.Char. Если бы это было преднамеренное решение, я бы ожидал такого же поведения на binary(1), который мог бы быть представлен System.Byte (но вместо этого это System.Linq.Binary с длиной 1.

Изменить 2: В случае, если это имеет значение, я использую LINQPad для просмотра созданного SQL. Я предполагал, что Linqpad будет использовать систему LinqToSQL, но сегодня я понял, что это предположение может быть ошибочным.

Редактирование 3: Я провел быстрый VS-проект, чтобы протестировать систему LinqToSQL, и получим тот же результат:

Код:

static void Main(string[] args)
{
    var db = new DataClasses1DataContext {Log = Console.Out};
    Console.Out.WriteLine("l.Side == 'A'");
    Console.Out.WriteLine("=============");
    Console.Out.WriteLine();
    foreach (Lot ll in db.Lots.Where(l => l.Side == 'A'))
    {
        break;
    }
    Console.Out.WriteLine();
    Console.Out.WriteLine("---------------------------------------");
    Console.Out.WriteLine();

    Console.Out.WriteLine("l.Side.Equals('A')");
    Console.Out.WriteLine("==================");
    Console.Out.WriteLine();
    foreach (Lot ll in db.Lots.Where(l => l.Side.Equals('A')))
    {
        break;
    }
    Console.In.Read();
}

Вывод:

l.Side == 'A'
=============

SELECT ..., [t0].[Side], ...
FROM [dbo].[Lot] AS [t0]
WHERE UNICODE([t0].[Side]) = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [65]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.6.1532.0


---------------------------------------

l.Side.Equals('A')
==================

SELECT ..., [t0].[Side], ...
FROM [dbo].[Lot] AS [t0]
WHERE [t0].[Side] = @p0
-- @p0: Input Char (Size = 1; Prec = 0; Scale = 0) [A]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.6.1532.0

Интересно отметить, что в версии == 'A' параметр передается как int, тогда как в версии .Equals он передается как char.

В этом контексте создаются файлы dbml и table script.

4b9b3361

Ответ 1

Существует документация об этом:

Несовпадения в SQL Server: Типы символов фиксированной длины. Transact-SQL различает категории Unicode и не Unicode и имеет три разных типа в каждой категории: фиксированная длина nchar/ char, переменная длина nvarchar/varchar и ntext/text большего размера. Типы символов фиксированной длины могут быть сопоставлены с типом CLR System.Char для извлечения символов, но они не соответствуют одному и тому же типу в преобразованиях и поведении.

И исходный код L2S имеет только одно место, которое использует строковый литерал "UNICODE":

введите описание изображения здесь

Похоже, что необходимым условием для функции является дерево синтаксиса SqlUnary node с типом Convert:

введите описание изображения здесь

Я не знаю, как вам удалось удовлетворить условие IsNumeric. Я думаю, что у вас есть несоответствие типов. Является ли столбец действительно отображаемым как System.Char?

Вероятно, вызов Equals не запускает этот путь кода. Вероятно, это ошибка L2S.

Equals транслируется в нескольких местах в исходном коде. Вот один из них:

введите описание изображения здесь

Похоже, что это обходит любые преобразования аргументов. Меня не волнует аргумент. Это, вероятно, не с множеством запросов (так что это скорее ошибка). Интересно, что произойдет, если вы напишете l.Side.Equals(1.2m). Я думаю, это переводится буквально в SQL.


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