Сложный запрос LINQ to SQL - PullRequest
       29

Сложный запрос LINQ to SQL

1 голос
/ 16 сентября 2009

У меня есть 3 таблицы: Принципал (Principal_ID, Scale), Частота (Frequency_ID, Value) и Посещение (Visit_ID, Principal_ID, Frequency_ID). Мне нужен запрос, который возвращает все принципалы (в таблице принципалов), и для каждой записи запрашивает емкость, необходимую для этого принципала, рассчитанную следующим образом:

Capacity = (Principal.Scale == 0 ? 0 : (Frequency.Value == 1 ? 1 : Frequency.Value * 1.8) / Principal.Scale)

Я использую LINQ to SQL, поэтому вот запрос:

from Principal p in ShopManagerDataContext.Instance.Principals
     let cap =
     (
          from Visit v in p.Visits
          let fqv = v.Frequency.Value
         select (p.Scale != 0 ? ((fqv == 1.0f ? fqv : fqv * 1.8f) / p.Scale) : 0)
     ).Sum()
     select new
     {
          p,
          Capacity = cap
     };

Сгенерированный TSQL:

SELECT [t0].[Principal_ID], [t0].[Name], [t0].[Scale], (
    SELECT SUM(
        (CASE 
            WHEN [t0].[Scale] <> @p0 THEN (
                (CASE 
                    WHEN [t2].[Value] = @p1 THEN [t2].[Value]
                    ELSE [t2].[Value] * @p2
                 END)) / (CONVERT(Real,[t0].[Scale]))
            ELSE @p3
         END))
    FROM [Visit] AS [t1]
    INNER JOIN [Frequency] AS [t2] ON [t2].[Frequency_ID] = [t1].[Frequency_ID]
    WHERE [t1].[Principal_ID] = [t0].[Principal_ID]
    ) AS [Capacity]
FROM [Principal] AS [t0]

И получаю ошибку:

SqlException: Multiple columns are specified in an aggregated expression containing an outer reference. If an expression being aggregated contains an outer reference, then that outer reference must be the only column referenced in the expression.

А идеи как решить это, если возможно, одним запросом?

Большое спасибо заранее!

Ответы [ 2 ]

1 голос
/ 17 сентября 2009

На основании комментария у меня есть альтернативное предложение. Так как ваша ошибка исходит от SQL, и вы не используете новый столбец в качестве фильтра, вы можете перенести свои вычисления на клиента. Чтобы это работало, вам нужно извлечь все соответствующие записи (используя DataLoadOptions.LoadWith <> в вашем контексте).

Чтобы удовлетворить ваше желание использовать привязку к DataGrid, вероятно, было бы проще спрятать всю сложность в свойстве Принципала.

partial class Principal
{
    public decimal Capacity
    {
        get
        {
            return this.Scale == 0 ? 0 : this.Visits.Select(v => 
                (v.Frequency.Value == 1 ? 1 : v.Frequency.Value * 1.8) / this.Scale).Sum();
        }
    }   
}

Тогда ваш поиск будет действительно простым:

using (ShopManagerDataContext context = new ShopManagerDataContext())
{
    DataLoadOptions options = new DataLoadOptions();
    options.LoadWith<Principal>(p => p.Visits);
    options.LoadWith<Visit>(v => v.Frequency);
    context.LoadOptions = options;

    return (from p in context.Principals
            select p).ToList();
}
1 голос
/ 16 сентября 2009

Вот два способа сделать это, изменив подход:

  1. Создайте пользовательскую агрегатную функцию, используя SQL CLR. Это может быть неправильным решением для вас, но, как указано, идеально подходит для решения проблемы. С одной стороны, это переместит всю логику на уровень данных, поэтому LINQ будет иметь ограниченную ценность. При таком подходе вы получаете эффективность, но она сильно влияет на вашу архитектуру.
  2. Загрузка таблиц посещений и периодичности в типизированный набор данных и использование LINQ для наборов данных. Это, вероятно, будет работать с использованием существующего кода, но я не пробовал. При таком подходе ваша архитектура более или менее сохраняется, но вы можете получить большой удар по эффективности, если число посещений и частота велики.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...