LINQ LEFT OUTER JOIN,WHERE& MIN / MAX /计数难度

时间:2011-05-21 00:55:01

标签: c# linq linq-to-sql

我是LINQ的新手,我发现在理解每个LINQ语句串在一起的数据转换流程时有点困难。我有一个相当标准的类型SQL语句,我很难获得类似的LINQ语句。

加入和分组似乎相当直接,但是当我最终尝试将Select语句放入并放入两个表中的所有各个字段时,事情似乎对我来说是分崩离析的。我希望我可以发布一些SQL代码,然后我尝试将LINQ表达式拼凑在一起,这里的人们可以回答我的问题如何基本上应该可行。很确定LINQ全部被打败了。 : - /

我有以下SQL语句,它为我提供了一些我想要使用并传递给网格控件的数据。 (然而,使用LINQ to Objects)

这是我试图在LINQ中模拟的SQL语句:

SELECT s.STRUCTURE_ID, s.TITLE, s.PHOTO, s.PHOTO_LINK, COUNT(s.STRUCTURE_ID)
   AS "How Many Activities", MIN(m.START_DATE) AS "START DATE", MAX(m.FINISH_DATE) AS "FINISH DATE"
FROM DB.STRUCTURES s
LEFT OUTER JOIN DB.ACTIVITIES m ON s.STRUCTURE_ID = m.STRUCTURE_ID
WHERE s.PARK = 'Elwood' AND m.CONTRACT_NO IS NOT NULL
GROUP BY  s.STRUCTURE_ID, s.TITLE, m.STATUS, s.PHOTO, s.PHOTO_LINK
ORDER BY s.STRUCTURE_ID

DB.Structures是ONE文件

DB.Activities是MANY文件

选择的目标是选择所有具有活动,匹配特定公园和DO签订合同的结构。额外的小扭曲是获得最早的Start& amp;完成每个结构的相关活动的日期。

生成的数据集应该是一个扁平的集合,我可以按以下格式将其移交给我的网格控件:

Structure ID     Description           Photo      Start Date     Finish Date
HL-100           Activity Room 100     Yes        6/6/2011       8/26/2011
HS-400A          Small Ones Gym        No         5/2/2011       6/30/2011

所以,我把它放在一起的LINQ如下:

var query = from s in db.Structures
            join a in db.Activities on s.Structure_ID equals a.Structure_ID into sa
            from allStructs in sa.DefaultIfEmpty()       // I believe this is how to get the outer join and flatten the selection?
            where s.PARK == 'Elwood' && allStructs.CONTRACT_NO != ''
            orderby s.Structure_ID
            group s by s.Structure_ID into g
            select new
            {
                myID = g.Key,
                myDesc = //I don't know how to display the s.Title here ,
                myPhoto = // I don't know how to display the s.Photo here ,
                myStartDate = g.Min(c => c.START_DATE),
                myEndDate = g.Max(c => c.FINISH_DATE)
            };

所以,现在问题是:

  1. 在LINQ中我不清楚的一点是,当连接完成时,似乎没有出现一个具有ALL DECLARED列的单个集合,就像我将使用标准SQL语句获得的那样返回选择。这对我来说很困惑。可能有人告诉我这是否属实?似乎我应该能够“看到”'allStructs'对象中的所有列......但显然情况并非如此。

  2. 我正在努力解决的另一个主要问题是如何显示ONE文件中的列 - 我不能简单地找到导航到这些字段的方法。 (与上面的#1有什么联系。)

  3. 最后,是否有一些好的“技巧”或工具可以在LINQ表达式中逐步执行每一行,就像在调试器中可以直接使用C#代码一样?当LINQ表达式命中VS中的常规调试器时,它只是突出显示已经退出的表达式,并且当事情得到设置/执行时,您无法真正看到正在发生的事情。 (我意识到这可能在这里要求很多。)我目前正在使用控制台在表达式启动后查看事物,例如执行query.Count()或类似的事情。

  4. 我真的很感激有助于更好地理解数据如何通过此LINQ流程进行转换。我一直在这里阅读很多,MSDN&买了几本书,但我还没有得到每个人似乎都会说的“灯泡时刻”。

    感谢所有......: - )

2 个答案:

答案 0 :(得分:1)

  1. join您有权访问s(当前结构)和sa之后,Structure_ID的活动组。在DefaultIfEmpty()之后,您可以访问相同的sallStructs,这与原始SQL示例中的m完全对应。你永远不会得到一个代表两个表中所有列的变量,但你也不会在SQL中得到它。

  2. 我相信LINQ to SQL足够聪明,可以在复合键上转换分组,在这种情况下,这就是你可能想要的:

    var query = from s in db.Structures
                join a in db.Activities on s.Structure_ID equals a.Structure_ID into sa
                from m in sa.DefaultIfEmpty() // LEFT OUTER JOIN
                where s.PARK == 'Elwood' && m.CONTRACT_NO != ''
                orderby s.Structure_ID
                group m by new { s.Structure_ID, s.Title, s.Photo } into g
                select new
                {
                    myID = g.Key.Structure_ID,
                    myDesc = g.Key.Title,
                    myPhoto = g.Key.Photo,
                    myStartDate = g.Min(c => c.START_DATE),
                    myEndDate = g.Max(c => c.FINISH_DATE)
                };
    

    请注意,为了与您的SQL保持一致,我将allStructs切换为m,并将group by切换为使用m而不是sm的值将包含在g组中,Key代表Structure_IDTitlePhoto的元组。

  3. 对于调试,最好的方法是打开一些日志记录(DataContext.Log属性)以查看正在生成的SQL。您还可以使用SQL事件探查器。它有时可以帮助逻辑上处理内存中的集合和LINQ to Objects,但需要注意的是,你可以在内存中实现根本不能转换为SQL的内容。

答案 1 :(得分:0)

要回答您的第二个问题,我认为您可以执行以下操作:

myDesc = g.First().Title
myPhoto = g.First().Photo