【问题标题】:What is the difference between the following queries? One is straight up SQL and the other is its equivalent in LINQ以下查询有什么区别?一个是直接的 SQL,另一个是 LINQ 中的等价物
【发布时间】:2013-01-11 15:08:19
【问题描述】:

我在这里有一个如下所示的 SQL 查询:

SELECT R.Extra3 AS 'Practice',
    SUM(DATEDIFF(s, R.Pickup, R.Hangup)) AS 'Seconds',
    COUNT(R.Extra3) AS 'Calls'
FROM Outbound.dbo.Results R
JOIN Outbound.dbo.Queue Q
    ON Q.QueueID = R.QueueID
    AND Q.Attempt = R.Attempt
WHERE R.CampaignId = 1
    AND DATEPART(m, R.Pickup) = DATEPART(m, DATEADD(m, -1, getdate()))
    AND DATEPART(y, R.Pickup) = DATEPART(y, DATEADD(m, -1, getdate()))
GROUP BY R.Extra3

我必须在程序中使用它并决定采用 LINQ 路线。所以这就是我想出的:

IQueryable<PracticeSummary> query = db.Results
    .Join(
        db.Queues,
        r => new { Id = r.QueueID.Value, Attempt = r.Attempt.Value },
        q => new { Id = q.QueueID, Attempt = (byte)q.Attempt },
        (r, q) => r
    )
    .Where(
        r => r.CampaignID == 1
            && r.PickUp.Value.Month == lastMonth
            && r.PickUp.Value.Year == lastMonthYear
    )
    .GroupBy(g => g.Extra3)
    .Select(r => new PracticeSummary
    {
        Practice = r.Key,
        Calls = r.Count(),
        Seconds = (r.Sum(item => EntityFunctions.DiffSeconds(item.PickUp, item.HangUp).Value))
    });

我的 SQL 查询给了我正确的结果,而我的 LINQ 查询返回的行数超过了 10 倍,因此总和和计数更多。

我什至查看了生成的 TSQL。它看起来像这样:

SELECT 
1 AS [C1], 
[GroupBy1].[K1] AS [Extra3], 
[GroupBy1].[A1] AS [C2], 
[GroupBy1].[A2] AS [C3]
FROM ( SELECT 
        [Filter1].[K1] AS [K1], 
        COUNT([Filter1].[A1]) AS [A1], 
        SUM([Filter1].[A2]) AS [A2]
        FROM ( SELECT 
                [Extent1].[Extra3] AS [K1], 
                1 AS [A1], 
                DATEDIFF (second, [Extent1].[PickUp], [Extent1].[HangUp]) AS [A2]
                FROM  [dbo].[Results] AS [Extent1]
                INNER JOIN [dbo].[Queue] AS [Extent2] ON ([Extent1].[QueueID] = [Extent2].[QueueID]) AND (([Extent1].[Attempt] =  CAST( [Extent2].[Attempt] AS tinyint)) OR (([Extent1].[Attempt] IS NULL) AND ( CAST( [Extent2].[Attempt] AS tinyint) IS NULL)))
                WHERE (1 = [Extent1].[CampaignID]) AND ((DATEPART (month, [Extent1].[PickUp])) = @p__linq__0) AND ((DATEPART (year, [Extent1].[PickUp])) = @p__linq__1)
        )  AS [Filter1]
        GROUP BY [K1]
)  AS [GroupBy1]

据我所知,它与我拥有和想要的非常相似。

所以我的问题是为什么结果不同?我的 SQL 和 LINQ 查询有什么区别?我错过了什么?

提前感谢您的时间和精力!

编辑:

这里是队列和结果的类:

public partial class Queue
{
    public long QueueID { get; set; }
    public long CampaignID { get; set; }
    public int Attempt { get; set; }
    public System.DateTime StartTime { get; set; }
    public System.DateTime EndTime { get; set; }
    public string Extra1 { get; set; }
    public string Extra2 { get; set; }
    public string Extra3 { get; set; }
}

public partial class Result
{
    public long ResultID { get; set; }
    public Nullable<long> QueueID { get; set; }
    public Nullable<long> CampaignID { get; set; }
    public Nullable<byte> Attempt { get; set; }
    public Nullable<System.DateTime> PickUp { get; set; }
    public Nullable<System.DateTime> HangUp { get; set; }
    public string Extra1 { get; set; }
    public string Extra2 { get; set; }
    public string Extra3 { get; set; }
}

【问题讨论】:

  • 您的 Q 和 R 表中是否有记录具有匹配的 QueueID 但 Attempt 为 NULL?另外,能否请您发布您的 Q 和 R 课程?
  • 请查看更新后的问题。我已经发布了课程。也没有尝试为 NULL 的记录。

标签: sql linq entity-framework tsql join


【解决方案1】:

如果您获得的结果数量是十倍,则表明问题出在联接中。我建议自下而上构建查询并测试结果以确定连接是否存在问题。

先试试这个:

IQueryable<PracticeSummary> query = db.Results
    .Join(
        db.Queues,
        r => new { Id = r.QueueID.Value, Attempt = r.Attempt.Value },
        q => new { Id = q.QueueID, Attempt = (byte)q.Attempt },
        (r, q) => new { ResultID = r.ResultID, QueueID = q.QueueID
    );

foreach (var result in query)
{
    // See what you have got
}

也许先做 where 会更容易:

IQueryable<PracticeSummary> query = db.Results
    .Where(
        r => r.CampaignID == 1
            && r.PickUp.Value.Month == lastMonth
            && r.PickUp.Value.Year == lastMonthYear
    )
    .Join(
        db.Queues,
        r => new { Id = r.QueueID.Value, Attempt = r.Attempt.Value },
        q => new { Id = q.QueueID, Attempt = (byte)q.Attempt },
        (r, q) => new { ResultID = r.ResultID, QueueID = q.QueueID
    );

foreach (var result in query)
{
    // See what you have got
}

【讨论】:

  • 知道了!我的查询不一样。关键在我的查询条件中。我的 LINQ 查询是正确的,而 SQL 是错误的。我正在查询最近几个月的记录。所以在 LINQ 中我做对了:&amp;&amp; r.PickUp.Value.Month == lastMonth &amp;&amp; r.PickUp.Value.Year == lastMonthYear 但在我的 SQL 查询中我有:AND DATEPART(m, R.Pickup) = DATEPART(m, DATEADD(m, -1, getdate())) AND DATEPART(y, R.Pickup) = DATEPART(y, DATEADD(m, -1, getdate())) For DATEPART y is day of yearyy 表示。是的,这是我的错误。您自下而上的方法对我有所帮助,因此我将您的方法标记为答案。谢谢!
【解决方案2】:

您的 Results.Attempt 和 Queue.Attempt 值是否可能包含一些空值??

如果是这样,下面的语句会将结果中的所有行与空值相乘:

OR (([Results].[Attempt] IS NULL) AND ( CAST( [Queue].[Attempt] AS tinyint) IS NULL))

【讨论】:

  • Queue 和 Results 表中没有 Attempt 为 NULL 值的记录。
  • 与您查询不同的另一件事是它使用 CAST([Queue].[Attempt] AS tinyint)) 而不仅仅是 [Queue].[Attempt] 但这不会影响任何事情如果尝试是整数。
猜你喜欢
  • 2021-09-06
  • 2013-04-14
  • 1970-01-01
  • 2012-10-29
  • 1970-01-01
  • 2019-07-17
  • 1970-01-01
  • 2012-01-02
  • 2021-11-17
相关资源
最近更新 更多