【问题标题】:How to join based on the top 1 id of another table using Linq to SQL?如何使用 Linq to SQL 基于另一个表的前 1 个 id 加入?
【发布时间】:2013-11-14 21:09:38
【问题描述】:

现在,我收到以下错误:

The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.

这是我的查询:

var issues = from issue in this.IssueDatas
             join original in this.NoteDatas on issue.NoteDatas
                                                     .OrderBy(n => n.CreatedDate)
                                                     .Select(n => n.NoteId)
                                                     .First() equals original.NoteId
             join current in this.NoteDatas on issue.NoteDatas
                                                    .OrderByDescending(n => n.CreatedDate)
                                                    .Select(n => n.NoteId)
                                                    .First() equals current.NoteId
             select { whatever, i, want, to, select }

获取这些 TOP 1 id 的 SQL 部分如下所示:

SELECT whatever, i, want, to
FROM [dbo].[issue_Details] AS [t0]
INNER JOIN [dbo].[issue_notes] AS [t1] ON ((
    SELECT TOP (1) [t3].[id]
    FROM (
        SELECT [t2].[id], [t2].[issue_id]
        FROM [dbo].[issue_notes] AS [t2]
        ORDER BY [t2].[CreatedDate]
        ) AS [t3]
    WHERE [t3].[issue_id] = [t0].[IssueDetailsId]
    )) = [t1].[id]
INNER JOIN [dbo].[issue_notes] AS [t4] ON ((
    SELECT TOP (1) [t6].[id]
    FROM (
        SELECT [t5].[id], [t5].[issue_id]
        FROM [dbo].[issue_notes] AS [t5]
        ORDER BY [t5].[CreatedDate] DESC
        ) AS [t6]
    WHERE [t6].[issue_id] = [t0].[IssueDetailsId]
    )) = [t4].[id]

...但它应该看起来更像这样:

FROM [dbo].[issue_Details] AS [t0]
INNER JOIN [dbo].[issue_notes] AS [t1] ON (
    SELECT TOP (1) [t2].[id]
    FROM [dbo].[issue_notes] AS [t2]
    ORDER BY [t2].[CreatedDate]
    WHERE [t2].[issue_id] = [t0].[IssueDetailsId]
    ) = [t1].[id]
INNER JOIN [dbo].[issue_notes] AS [t4] ON (
    SELECT TOP (1) [t5].[id]
    FROM [dbo].[issue_notes] AS [t5]
    ORDER BY [t5].[CreatedDate] DESC
    WHERE [t5].[issue_id] = [t0].[IssueDetailsId]
    ) = [t4].[id]

我尝试使用 this.NoteDatas 而不是 issue.NoteDatas 并手动应用 id 过滤器,我尝试选择第一个注释然后获取 id(反转我在上面输入的内容),我尝试过使用Take(int) 而不是First()... 我只是不知道该怎么做。 LINQ 的读取比它生成的 SQL 更直接。

【问题讨论】:

  • 我猜,LINQ 生成的查询返回 0 行还是 1 行?
  • 此查询的用例是返回问题列表,其中包含有关其最早注释和最新注释的信息。
  • 产生的 SQL 和你的 SQL 是一样的,唯一的区别是产生了多一列(可能是为了进一步使用),而你放错了 WHERE(应该在 ORDER BY 之前)。还有什么不同?
  • 好的,现在将为您的问题制作 LINQ。
  • 是的,我确实放错了 WHERE;我的第二个示例只是为了表明在 Linq to SQL 产生两个的 ON 中只需要发生一个 SELECT。

标签: c# .net sql linq-to-sql


【解决方案1】:

这就是我最终更改 LINQ 以使其工作的方式:

var issues = from issue in this.IssueDatas
             join original in this.NoteDatas on issue.NoteDatas
                                                     .Min(n => n.CreatedDate) equals original.CreatedDate
             join current in this.NoteDatas on issue.NoteDatas
                                                    .Max(n => n.CreatedDate) equals current.CreatedDate
             select { whatever, i, want, to, select }

【讨论】:

  • 更新:Linq-to-database 对我来说已经走上了渡渡鸟的道路,我厌倦了不必要的复杂性和抽象泄漏。
【解决方案2】:

通过这个:

 join original in this.NoteDatas on issue.NoteDatas
                                         .OrderBy(n => n.CreatedDate)
                                         .Select(n => n.NoteId)
                                         .First() equals original.NoteId

您说的是“按 CreatedDate 排序,从结果中取出第一行并检查它是否等于 NoteId”。这被正确地呈现为:

INNER JOIN [dbo].[issue_notes] AS [t1] ON ((
    SELECT TOP (1) [t3].[id]
    FROM (
        SELECT [t2].[id], [t2].[issue_id]
        FROM [dbo].[issue_notes] AS [t2]
        ORDER BY [t2].[CreatedDate]
        ) AS [t3]
    WHERE [t3].[issue_id] = [t0].[IssueDetailsId]
    )) = [t1].[id]

所以t2.id 对于任何外部[t1].[id] 始终相同,但仅取决于[t0].[IssueDetailsId]

示例:

    var issues = new Tuple<int, string>[]
        {
            new Tuple<int, string>(1, "aaa"),
            new Tuple<int, string>(2, "bbb")
        };
    var notes = new Tuple<int, DateTime, string>[]
        {
            new Tuple<int, DateTime, string>(1, DateTime.Parse("01/01/2001"), "earliest for 1"),
            new Tuple<int, DateTime, string>(1, DateTime.Parse("02/01/2001"), "middle for 1"),
            new Tuple<int, DateTime, string>(1, DateTime.Parse("03/01/2001"), "latest for 1"),
            new Tuple<int, DateTime, string>(2, DateTime.Parse("10/01/2001"), "earliest for 2"),
            new Tuple<int, DateTime, string>(2, DateTime.Parse("11/01/2001"), "middle for 2"),
            new Tuple<int, DateTime, string>(2, DateTime.Parse("12/01/2001"), "once more middle for 2"),
            new Tuple<int, DateTime, string>(2, DateTime.Parse("13/01/2001"), "latest for 2")
        };

        var result =
            ctx.Set<Parent>().Select(i => new
                {
                    i.Id,
                    e = ctx.Set<Child>().Where(c => c.ParentId == i.Id).OrderBy(c => c.Name).FirstOrDefault(),
                    l = ctx.Set<Child>().Where(c => c.ParentId == i.Id).OrderByDescending(c => c.Name).FirstOrDefault()
                });

【讨论】:

  • 使用issue.NoteDatas 产生与this.NoteDatas.Where(n =&gt; n.IssueId == issue.IssueId) 相同的输出。无论哪种方式,我的意思是“获取此问题 ID 的注释,按创建日期排序,并获取第一个注释 ID”,但 LINQ to SQL 输出的做法略有不同。即使我改用issue.NoteDatas.OrderBy(n =&gt; n.CreatedDate).First().NoteId equals original.NoteId,我也会看到相同的输出。
  • 这会产生一个可以解析的查询,但恐怕这是一个非常低效且无法使用的查询,因为它会为我要选择的每一列评估SELECT TOP 1 id FROM notes ORDER BY...
  • @tuespetre 请分享 SQL
  • @tuespetre 无论如何都会为每个问题完成两个子选择,这是查找日期的唯一方法(您的 TOP 1 与 order by 也是如此)。需要检查索引使用的执行计划。
  • @tuespetre 稍微更新了结果。它呈现为非常好的 SQL,例如 SELECT 字段和两个 OUTER APPLY - 这真的很好。试试看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多