代码之家  ›  专栏  ›  技术社区  ›  P Daddy

SQL CE上的LINQ聚合左连接

  •  1
  • P Daddy  · 技术社区  · 16 年前

    SELECT I.InvoiceID, I.CustomerID, I.Amount AS AmountInvoiced,
           I.Date AS InvoiceDate, ISNULL(SUM(P.Amount), 0) AS AmountPaid,
           I.Amount - ISNULL(SUM(P.Amount), 0) AS AmountDue
    FROM Invoices I
    LEFT JOIN Payments P ON I.InvoiceID = P.InvoiceID
    WHERE I.Date between @start and @end
    GROUP BY I.InvoiceID, I.CustomerID, I.Amount, I.Date
    ORDER BY AmountDue DESC
    

    我提出的最佳等效LINQ表达式花费了我更长的时间:

    var invoices = (
        from I in Invoices
        where I.Date >= start &&
              I.Date <= end
        join P in Payments on I.InvoiceID equals P.InvoiceID into payments
        select new{
            I.InvoiceID, I.CustomerID, AmountInvoiced = I.Amount, InvoiceDate = I.Date,
            AmountPaid = ((decimal?)payments.Select(P=>P.Amount).Sum()).GetValueOrDefault(),
            AmountDue = I.Amount - ((decimal?)payments.Select(P=>P.Amount).Sum()).GetValueOrDefault()
        }
    ).OrderByDescending(row=>row.AmountDue);
    

    当在SQL Server上运行时,将获得等效的结果集。但是,使用SQLCE数据库会改变一些事情。T-SQL几乎保持不变。我只需要换衣服 ISNULL COALESCE . 但是,使用相同的LINQ表达式会导致错误:

    There was an error parsing the query. [ Token line number = 4,
    Token line offset = 9,Token in error = SELECT ]

    因此,我们来看看生成的SQL代码:

    SELECT [t3].[InvoiceID], [t3].[CustomerID], [t3].[Amount] AS [AmountInvoiced], [t3].[Date] AS [InvoiceDate], [t3].[value] AS [AmountPaid], [t3].[value2] AS [AmountDue]
    FROM (
        SELECT [t0].[InvoiceID], [t0].[CustomerID], [t0].[Amount], [t0].[Date], COALESCE((
            SELECT SUM([t1].[Amount])
            FROM [Payments] AS [t1]
            WHERE [t0].[InvoiceID] = [t1].[InvoiceID]
            ),0) AS [value], [t0].[Amount] - (COALESCE((
            SELECT SUM([t2].[Amount])
            FROM [Payments] AS [t2]
            WHERE [t0].[InvoiceID] = [t2].[InvoiceID]
            ),0)) AS [value2]
        FROM [Invoices] AS [t0]
        ) AS [t3]
    WHERE ([t3].[Date] >= @p0) AND ([t3].[Date] <= @p1)
    ORDER BY [t3].[value2] DESC
    

    啊!好吧,所以当运行SQL Server时,它既丑陋又低效,但我们不应该在意,因为它是 想象上的 加快 工作 针对SQL CE,它显然不支持选择列表中的子查询。

    事实上,我在LINQ中尝试了几个不同的左连接查询,它们似乎都有相同的问题。甚至:

    from I in Invoices
    join P in Payments on I.InvoiceID equals P.InvoiceID into payments
    select new{I, payments}
    

    生成:

    SELECT [t0].[InvoiceID], [t0].[CustomerID], [t0].[Amount], [t0].[Date], [t1].[InvoiceID] AS [InvoiceID2], [t1].[Amount] AS [Amount2], [t1].[Date] AS [Date2], (
        SELECT COUNT(*)
        FROM [Payments] AS [t2]
        WHERE [t0].[InvoiceID] = [t2].[InvoiceID]
        ) AS [value]
    FROM [Invoices] AS [t0]
    LEFT OUTER JOIN [Payments] AS [t1] ON [t0].[InvoiceID] = [t1].[InvoiceID]
    ORDER BY [t0].[InvoiceID]
    

    这也会导致错误:

    There was an error parsing the query. [ Token line number = 2,
    Token line offset = 5,Token in error = SELECT ]

    1 回复  |  直到 16 年前
        1
  •  3
  •   dahlbyk    16 年前

    您是否尝试过使用 group by

    var invoices =
        from I in Invoices
        where I.Date >= start && I.Date <= end
        join P in Payments on I.InvoiceID equals P.InvoiceID into J
        group J.Sum(p => p.Amount) by new { I.InvoiceID, I.CustomerID, I.Amount, I.Date } into G
        let AmountPaid = G.Sum()
        let AmountDue = G.Key.Amount - AmountPaid
        orderby AmountDue descending
        select new
        {
            G.Key.InvoiceID,
            G.Key.CustomerID,
            AmountInvoiced = G.Key.Amount,
            InvoiceDate = G.Key.Date,
            AmountPaid,
            AmountDue
        };
    

    结果与内存中的集合正好相反:

    var Invoices = new[] {
        new { InvoiceID = 1, CustomerID = 2, Amount = 2.5m, Date = DateTime.Today },
        new { InvoiceID = 2, CustomerID = 3, Amount = 5.5m, Date = DateTime.Today }
    }.AsQueryable();
    var Payments = new[] {
        new { InvoiceID = 1, Amount = 1m }
    }.AsQueryable();
    

    { InvoiceID = 2, CustomerID = 3, AmountInvoiced = 5.5, InvoiceDate = 8/15/2009,
      AmountPaid = 0, AmountDue = 5.5 }
    { InvoiceID = 1, CustomerID = 2, AmountInvoiced = 2.5, InvoiceDate = 8/15/2009, 
      AmountPaid = 1, AmountDue = 1.5 }
    

    如果这不起作用,LINQ左连接通常使用 DefaultIfEmpty() 关于连接结果。您可能需要这样做:

    var invoices =
        from I in Invoices
        where I.Date >= start && I.Date <= end
        join P in Payments on I.InvoiceID equals P.InvoiceID into J
        from PJ in J.DefaultIfEmpty() // Left Join
        group PJ by new { I.InvoiceID, I.CustomerID, I.Amount, I.Date } into G
        let AmountPaid = G.Sum(p => p == null ? 0 : p.Amount)
        // etc...