SqlException об СОЮЗЕ, ИНТЕРСЕКТЕ И ИСКЛЮЧЕНИИ - PullRequest
6 голосов
/ 16 марта 2009

Может ли кто-нибудь помочь мне с этим исключением? Я не понимаю, что это значит или как это исправить ... Это SqlException со следующим сообщением:

Все запросы, объединенные с использованием оператора UNION, INTERSECT или EXCEPT, должны иметь одинаковое количество выражений в своих целевых списках.

Я получаю его при выполнении запроса в псевдокоде, который выглядит следующим образом:

// Some filtering of data
var query = data.Subjects
            .Where(has value)
            .Where(has other value among some set of values);

// More filtering, where I need to have two different options
var a = query
            .Where(some foreign key is null);
var b = query
            .Where(some foreign key is not null)
            .Where(and that foreign key has a property which is what I want);
query = a.Union(b);

// Final filter and then get result as a list
var list = query
            .Where(last requirement)
            .ToList();

Если я удаляю a.Union(b) детали, он запускается без исключения. Так что я знаю, что ошибка есть. Но почему я это понимаю? И как я могу это исправить? Я делаю что-то слишком сумасшедшее здесь? Я неправильно понял, как использовать вещь Union?

По сути, у меня есть несколько сущностей, которые имеют внешний ключ для какой-то другой сущности. И мне нужно получить все объекты, у которых либо внешний ключ установлен на null, либо если этот внешний объект удовлетворяет некоторым требованиям.

Ответы [ 8 ]

9 голосов
/ 23 июля 2009

Судя по указанной вами ошибке SQL, у вас может возникнуть та же проблема, что и у меня. В основном, когда запросы Linq to SQL, использующие метод расширения Concat или Union для двух разных запросов, обнаруживаются ошибки в Linq to SQL, которые оптимизируют каждую проекцию отдельно, независимо от того, что проекция должна оставаться прежней для выполнения Союз SQL.

Ссылки:

LINQ to SQL выдает некорректный TSQL при использовании UNION или CONCAT

Linq to SQL Union Ошибка генерации одинакового имени поля

Если это тоже ваша проблема, я нашел решение, которое работает для меня, как показано ниже.

var queryA = 
    from a in context.TableA
    select new 
    {
        id,
        name,
        onlyInTableA,
    }

var queryB = 
    from b in context.TableB
    let onlyInTableA = default(string)
    select new 
    {
        id,
        name,
        onlyInTableA,
    }

var results = queryA.Union(queryB).ToList();
4 голосов
/ 17 марта 2009

Поскольку это похоже на проблему с сгенерированным SQL, вы должны попытаться использовать SQL Profiler или этот код для класса DebuggerWritter , чтобы записать SQL в окно вывода в Visual Studio.

Ошибка SQL обычно вызвана тем, что поля, извлеченные для UNION, не совпадают для двух запросов. Например, если первый запрос может иметь 3 поля, а второй запрос имеет 4 поля, эта ошибка возникнет. Таким образом, просмотр сгенерированного SQL определенно поможет в этом случае.

0 голосов
/ 26 мая 2010

Ну, у меня была проблема с этим. Используя Sql 08, я имел две табличные функции, которые возвращали int и строку в обоих случаях. Я создал сложный объект и использовал linq для попытки объединения. Пришлось сравнить IEqualityComparer. Все скомпилировано нормально, но вылетело с неподдерживаемой перегрузкой. Хорошо, я понял, что обсуждаемая проблема, похоже, привкуса к проигрышной казни. Поэтому я получаю коллекции и помещаю ToList (), затем выполняю UNION, и все это хорошо. Не уверен, что это полезно, но у меня работает

0 голосов
/ 10 ноября 2009

У jpierson правильно сформулирована проблема.
У меня также была проблема, на этот раз вызванная некоторыми литералами в операторе select:
Dim results = (From t in TestDataContext.Table1 _<br> Where t.ID = WantedID _<br> Select t.name, SpecialField = 0, AnotherSpecialField = 0, t.Address).Union _<br> From t in TestDataContext.Table1 _<br> Where t.SecondID = WantedSecondID _<br> Select t.name, SpecialField = 1, AnotherSpecialField = 0, t.Address)

Первый подзапрос «SpecialField = 0» и «AnotherSpecialField = 0» был оптимизирован, в результате чего в объединении использовалось одно поле вместо двух, что, очевидно, завершится ошибкой.
Мне пришлось изменить первый запрос, чтобы значения SpecialField & AnotherSpecialField отличались, как во втором подзапросе.

0 голосов
/ 18 марта 2009

Случайно ли вы передаете значение переменной 'select' в переменной или вы возвращаете одно и то же поле более одного раза? В SP1 появилась ошибка, когда он пытается «оптимизировать» такие вещи, что может привести к сбою запросов объединения (из-за того, что части запроса «оптимизируют» различные передаваемые параметры).

Если вы публикуете свой фактический запрос, а не псевдокод, вам будет легче определить, так ли это.

(И обходной путь, если это так, состоит в том, чтобы сначала материализовать отдельные детали, а затем выполнить объединение на стороне клиента (L2O)).

0 голосов
/ 17 марта 2009

Я бы назвал data.GetCommand(query) и проанализировал бы полученный DbCommand (особенно сгенерированную строку SQL). Это должно дать вам ключ к пониманию того, что идет не так.

Нет проекции, поэтому я ожидаю, что оба списка целей будут одинаковыми.

Вы можете попытаться уменьшить свой запрос до меньшего, который все еще не работает. Начните с query.Union(query) (это должно как минимум работать). Затем добавьте ваши Where звонки один за другим, чтобы увидеть, когда он перестанет работать.

Это должен быть один из ваших вызовов Where, который добавляет дополнительные столбцы в ваш список выбора.

0 голосов
/ 16 марта 2009

query = a.Union (b);

Не очень хорошая идея изменять захваченные переменные ... Вероятно, причина ошибки.

ОБНОВЛЕНИЕ: хорошо, не

Вот еще одна идея. Подсказка в сообщении об ошибке.

var a = query
         .Where(some foreign key is null)
         .Select(x => x);

Или играйте, добавив еще одну «фальшивку», где они станут равными

0 голосов
/ 16 марта 2009

Можете ли вы написать это в одном запросе?

.Where(row => row.ForeignKey == null || row.ForeignKey.SomeCondition);

Существуют также способы объединения выражений (OrElse), но это не тривиально.

Не знаю, откуда возникла ошибка!

edit: не проверял, но это должно быть логически эквивалентно UNION:

public static IQueryable<T> WhereAnyOf<T>(
    this IQueryable<T> source,
    params Expression<Func<T, bool>>[] predicates)
{
    if (source == null) throw new ArgumentNullException("source");
    if (predicates == null) throw new ArgumentNullException("predicates");
    if (predicates.Length == 0) return source.Where(row => false);
    if (predicates.Length == 1) return source.Where(predicates[0]);

    var param = Expression.Parameter(typeof(T), "row");
    Expression body = Expression.Invoke(predicates[0], param);
    for (int i = 1; i < predicates.Length; i++)
    {
        body = Expression.OrElse(body,
            Expression.Invoke(predicates[i], param));
    }
    return source.Where(Expression.Lambda<Func<T, bool>>(body, param));
}
...