LINQ to SQL复杂查询问题

时间:2009-09-16 19:26:04

标签: c# sql-server linq-to-sql

我有3个表:Principal(Principal_ID,Scale),Frequency(Frequency_ID,Value)和Visit(Visit_ID,Principal_ID,Frequency_ID)。 我需要一个返回所有主体的查询(在Principal表中),并且对于每条记录,查询该主体所需的容量,计算如下:

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 个答案:

答案 0 :(得分:1)

通过更改方法,有以下两种方法:

  1. 使用SQL CLR创建用户定义的聚合函数。这可能不是适合您的解决方案,但它完全适合所述问题。首先,这会将所有逻辑移动到数据层中,因此LINQ的价值有限。通过这种方法,您可以获得效率,但这会对您的架构产生重大影响。
  2. 将访问和频率表加载到类型化的DataSet中,并使用LINQ to数据集。这可能会使用您现有的代码,但我还没有尝试过。通过这种方法,您的架构或多或少得到了保留,但如果访问量和频率很高,您可以大大提高效率。

答案 1 :(得分:1)

根据评论,我有另一个建议。由于您的错误来自SQL,并且您没有使用新列作为过滤器,因此您可以将计算移动到客户端。为此,您需要提取所有相关记录(在您的上下文中使用DataLoadOptions.LoadWith&lt;&gt;)。

为了进一步使用绑定到DataGrid的愿望,将复杂性埋没在Principal的属性中可能是最容易的。

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();
}