【问题标题】:How to limit a LINQ left outer join to one row如何将 LINQ 左外连接限制为一行
【发布时间】:2010-10-04 07:24:51
【问题描述】:

我有一个左外连接(如下)按预期返回结果。我需要将“正确”表中的结果限制为“第一次”命中。我能以某种方式做到这一点吗?目前,我为两个表中的每条记录都得到了一个结果,无论我在右表(照片)中有多少结果,我都只想从左边的表(项目)中看到一个结果。

        var query = from i in db.items
                join p in db.photos
                on i.id equals p.item_id into tempPhoto
                from tp in tempPhoto.DefaultIfEmpty()
                orderby i.date descending 
                select new
                {
                    itemName = i.name,
                    itemID = i.id,
                    id = i.id,
                    photoID = tp.PhotoID.ToString()
                };


    GridView1.DataSource = query;
    GridView1.DataBind();

【问题讨论】:

    标签: c# asp.net database linq join


    【解决方案1】:

    这将为您完成这项工作。

    from i in db.items
    let p = db.photos.Where(p2 => i.id == p2.item_id).FirstOrDefault()
    orderby i.date descending
    select new
    {
      itemName = i.name,
      itemID = i.id,
      id = i.id,
      photoID = p == null ? null : p.PhotoID.ToString();
    }
    

    当我针对我自己的模型生成它时,我得到了这个 sql(并且在投影中没有 name 和第二个 id 列)。

    SELECT [t0].[Id] AS [Id], CONVERT(NVarChar,(
        SELECT [t2].[PhotoId]
        FROM (
            SELECT TOP (1) [t1].[PhotoId]
            FROM [dbo].[Photos] AS [t1]
            WHERE [t1].[Item_Id] = ([t0].[Id])
            ) AS [t2]
        )) AS [PhotoId]
    FROM [dbo].[Items] AS [t0]
    ORDER BY [t0].[Id] DESC
    

    当我询问计划时,显示子查询是通过这个join实现的:

    <RelOp LogicalOp="Left Outer Join" PhysicalOp="Nested Loops">
    

    【讨论】:

    • 我喜欢这个解决方案的优雅,但是我认为这可能会创建一个查询,由于子选择,SQL 难以优化
    • 我检查并满意生成的 SQL 和估计的执行计划。子选择被计划为左外连接。
    • 很酷,你有机会发布 SQL,我很想看到它。
    • 因为我一直在处理子表组。如果你的方式可行,我会对苗条的代码更满意。但就像我想要性能最好的解决方案一样,而不是最漂亮的。
    • 不错,我必须给你道具。你介意运行我的,我没有模型设置。你应该有足够的代表来编辑我的评论,添加执行计划。
    【解决方案2】:

    您要做的是对表格进行分组。最好的方法是:

        var query = from i in db.items
                    join p in (from p in db.photos
                               group p by p.item_id into gp
                               where gp.Count() > 0
                               select new { item_id = g.Key, Photo = g.First() })
                on i.id equals p.item_id into tempPhoto
                from tp in tempPhoto.DefaultIfEmpty()
                orderby i.date descending 
                select new
                {
                    itemName = i.name,
                    itemID = i.id,
                    id = i.id,
                    photoID = tp.Photo.PhotoID.ToString()
                };
    

    编辑:这是 Amy B 说话。我这样做只是因为尼克要求我这样做。 Nick,请在您认为合适的情况下修改或删除此部分。

    生成的 SQL 很大。 int 0(与计数比较)通过参数传入。

    SELECT [t0].X AS [id], CONVERT(NVarChar(MAX),(
        SELECT [t6].Y
        FROM (
            SELECT TOP (1) [t5].Y
            FROM [dbo].[Photos] AS [t5]
            WHERE (([t4].Y IS NULL) AND ([t5].Y IS NULL)) OR (([t4].Y IS NOT NULL) AND ([t5].Y IS NOT NULL) AND ([t4].Y = [t5].Y))
            ) AS [t6]
        )) AS [PhotoId]
    FROM [dbo].[Items] AS [t0]
    CROSS APPLY ((
            SELECT NULL AS [EMPTY]
            ) AS [t1]
        OUTER APPLY (
            SELECT [t3].Y
            FROM (
                SELECT COUNT(*) AS [value], [t2].Y
                FROM [dbo].[Photos] AS [t2]
                GROUP BY [t2].Y
                ) AS [t3]
            WHERE (([t0].X) = [t3].Y) AND ([t3].[value] > @p0)
            ) AS [t4])
    ORDER BY [t0].Z DESC
    

    执行计划显示三个左连接。至少一个是微不足道的,不应该被计算(它带来了零)。这里有足够的复杂性,我无法清楚地指出任何效率问题。它可能会运行得很好。

    【讨论】:

    • 这可能不是 Linq to SQL 的答案,但它确实解决了 Entity Framework 的问题,产生了类似的 sql。现在正在寻找更好的解决方案。
    【解决方案3】:

    你可以这样做:

    var q = from c in
              (from s in args
               select s).First()
            select c;
    

    围绕查询的最后一部分。不确定它是否会起作用或会产生什么样的怪异 SQL :)

    【讨论】:

      【解决方案4】:

      使用内部查询。在没有照片的情况下包括DefaultIfEmpty,在多于一张的情况下包括orderby。以下示例拍摄id 最大的照片。

      var query = 
          from i in db.items
          let p = from p in db.photos where i.id == p.item_id orderby p.id select p).DefaultIfEmpty().Last()
          orderby i.date descending
          select new {
            itemName = i.name,
            itemID = i.id,
            id = i.id,
            photoID = p.PhotoID
          };
      

      如果需要专门处理无照片的情况,可以省略DefaultIfEmpty,改用FirstOrDefault/LastOrDefault

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-09-17
        • 2018-03-09
        • 2016-12-19
        • 2018-02-10
        • 2011-08-10
        • 1970-01-01
        • 1970-01-01
        • 2011-11-13
        相关资源
        最近更新 更多