【问题标题】:join without forgetting data that wasn't affected by the join加入而不会忘记不受加入影响的数据
【发布时间】:2014-01-21 20:17:44
【问题描述】:

我对三个表感兴趣,以下是相关信息:

+---------------+ +---------------+ +---------------+ +---------------+
| Class         | | Tests         | | StudentClasses| | Student       |
+---------------+ +---------------+ +---------------+ +---------------+
| ClassID       | | ClassID       | | ClassID       | | StudentID     |
|               | | WholeYearTest | | StudentID     | | StudentYear   |
|               | | TestYear      | |               | |               |
+---------------+ +---------------+ +---------------+ +---------------+

我有一个变量login,它是一个Student

我在 LINQ 中运行这个查询:

        from tests in App.db.Tests
        join studentClasses in App.db.StudentClasses on tests.ClassID equals studentClasses.ClassID
        where (
         (tests.WholeYearTest == true && tests.TestYear == login.StudentYear)
         || 
         studentClasses.StudentID == login.StudentID
        )
        select tests;

不幸的是,我只得到了符合条件studentClasses.StudentID == login.StudentID的结果

我想要什么:

  • 由于StudentYear 等于TestYearWholeYearTest 为真而学生必须参加的所有测试
  • 学生必须参加的所有测试,因为与其关联的可能多个StudentClasses 之一列在Tests.ClassID 下。

我认为这可能是由于对 JOIN 工作原理的公然误解,但我想不出任何其他方式来实现这一点。除了循环所有测试之外,任何人都可以提出实施建议吗?如果没有,我想我会使用循环,但我确信在 LINQ 中必须有某种方法来实现它。

【问题讨论】:

  • 是否可以在没有考试的情况下开设学生课程?因为如果您的第二个条件仅出现在班级没有测试的情况下,您将永远不会获得这些记录。加入将从结果集中删除这些记录。
  • 测试可以是全年(即第 6 年)或特定班级,由 tests.TestYear 规定。有什么方法可以阻止加入删除这些记录?
  • 理想情况下,我希望选择因学生一年的全年测试而导致的测试,或者具体适用于学生的任何班级。
  • Join 的目的其实就是删除那些记录 :)
  • 那么我有什么办法可以通过 LINQ 做我想做的事吗?我只是想使用 join 将另一个表带入等式,只是为了测试学生的班级关系。

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


【解决方案1】:

我认为这会满足您的需求:

from tests in App.db.Tests
join studentClasses in App.db.StudentClasses
on new { tests.ClassID, login.StudentID }
equals new { studentClasses.ClassID, studentClasses.StudentID }
into gj
from subStudentClass in gj.DefaultIfEmpty()
where (
 (tests.WholeYearTest == true && tests.TestYear == login.StudentYear)
 || 
 (subStudentClass != null && subStudentClass.StudentID == login.StudentID)
)
select tests

最大的不同是现在有了outer join,这样你仍然可以找到连接失败的结果。

另外,现在ClassIDStudentID 都可以加入,这样您就不会得到其他学生的匹配。

有以下测试数据:

var App = new { db = new {
    Tests = new[] {
        new Test { ClassID = 1, WholeYearTest = true, TestYear = 1999 },
        new Test { ClassID = 2, WholeYearTest = true, TestYear = 1999 },
        new Test { ClassID = 3, WholeYearTest = false, TestYear = 1999 },
        new Test { ClassID = 4, WholeYearTest = false, TestYear = 1999 },
    },
    StudentClasses = new[] {
        new StudentClass { ClassID = 1, StudentID = 1 },
        new StudentClass { ClassID = 1, StudentID = 2 },
        new StudentClass { ClassID = 4, StudentID = 1 },
        new StudentClass { ClassID = 3, StudentID = 2 },
    }
} };
var login = new { StudentID = 1, StudentYear = 1999 };

Console.WriteLine(string.Join(Environment.NewLine, (
    //above query
  ).Select(x => string.Join(",", x.ClassID, x.WholeYearTest, x.TestYear))));

打印出来

1,True,1999 
2,True,1999 
4,False,1999 

【讨论】:

  • 它突出显示“join”并给出错误:“join 子句中表达式之一的类型不正确。调用 'GroupJoin' 时类型推断失败。”知道发生了什么吗?非常感谢您的帮助!除了基础知识之外,我在数据库查询方面一直很糟糕,这完全超出了我的能力:P
  • 我认为您的属性名称和/或类型不匹配。例如。请参阅stackoverflow.com/questions/5563208/linq-to-sql-join-issues,了解如何在匿名类型中指定属性名称。
  • 啊;我将 .Value 附加到 int?值,并为它们设置属性名称,它似乎工作得很好!非常感谢这个,整个星期都会被困在这里。
【解决方案2】:

单个内连接是不够的;您的查询实际上需要使用内部联接和联合来执行。

试一试 - 我没有测试它,因为我认为你可以这样做:)

注意 - 您可能还需要在其中插入 Distinct()。

var q1 = (from tests in App.db.Tests
          join studentClasses in App.db.StudentClasses 
             on tests.ClassID equals studentClasses.ClassID
          select tests).Concat(App.db.Tests.Where(t=>t.WholeYearTest && t.TestYear == login.StudentYear));

【讨论】:

  • 感谢您的回答!我将接受蒂姆 S. 他首先回答的问题,但仍然感谢你们,非常感谢。
  • 没问题。仅供参考,虽然我认为 Tim 的回答非常棒,并且绝对值得为额外的测试付出努力,但我也认为应该使用最简单的解决方案来完成工作。迟早您将不得不编辑代码,并且您希望能够阅读它。 :)
猜你喜欢
  • 2021-12-28
  • 2011-07-16
  • 1970-01-01
  • 2021-04-13
  • 1970-01-01
  • 2014-11-22
  • 1970-01-01
  • 2019-07-07
  • 1970-01-01
相关资源
最近更新 更多