一段时间以来,我一直在尝试将此SQL转换为linq:
SELECT
Name
FROM
Director d
JOIN
Movie m ON d.Id = m.DirectorId
JOIN
MovieActor ma ON m.Id = ma.MovieId
WHERE
ReleaseDate <= '2005-12-31'
AND Rating >= 9
GROUP BY
Name
HAVING
COUNT(DISTINCT ma.ActorId) BETWEEN 3 AND 7
AND COUNT(DISTINCT CASE WHEN m.DurationMinutes >= 60 THEN m.DurationMinutes END) >= 2
;
这里是100%的工作部分,很简单。
var query4 = (from d in directors
join m in movies on d.Id equals m.DirectorId
join ma in movieActors on m.Id equals ma.MovieId
where m.ReleaseDate <= Convert.ToDateTime("2015-12-31")
where m.Rating >= 9
);
大多数情况下,我都在努力解决这些问题:
d, m, ma
称为单个表,而不是以某种方式连接3,因为在尝试group d by ... into g
时,我丢失了另外两个表。 linq中的HAVING
语法也不明显。
编辑: 我正在使用LINQ to Objects提供程序,这里有一些: 通过初始化的类,这些类是杂乱的容器
public Director(int _Id, string _Name)
public Movie(int _Id, string _Name, int _DurationMinutes, DateTime _ReleaseDate, int _DirectorId, int _Rating)
public Actor(int _Id, string _Name, int _Age)
public MovieActor(int _MovieId, int _ActorId)
我正在测试的数据(从这些数组中收集列表)
var directorArr = new (int, string)[] { (1, "Bebopvsky"), (2, "Tarrantino"), (3, "CubeRick") };
var actorArr = new (int, string, int)[]
{ (1, "Dat Maan",75), (2, "That Man", 28),
(3, "Dat Women", 32), (4, "That Women", 22),
(5, "Already Women", 12) };
var moviesArr = new (int, string, int, DateTime, int, int)[]
{ (1, "Platform for soul", 121, Convert.ToDateTime("2018-12-31"), 2, 9),
(2, "Full-featured access management", 42, Convert.ToDateTime("2019-01-01"), 3, 7),
(3, "Robust LDAP server for Java", 13, Convert.ToDateTime("2005-05-25"), 3, 4),
(4, "Man of Rain", 114, Convert.ToDateTime("2004-07-21"), 1, 10),
(5, "Man of Arms", 152, Convert.ToDateTime("2003-02-17"), 1, 9),
(6, "Man of War", 93, Convert.ToDateTime("2017-07-05"), 2, 8),
(7, "Man of Legs", 33, Convert.ToDateTime("2018-11-11"), 1, 9),
(8, "Mof", 55, Convert.ToDateTime("2015-11-11"), 2, 8) };
var movieActorArr = new (int, int)[]
{ (1,1), (1,3), (1,4), (1,5),
(2,1), (2,5),
(3,4),
(4,1), (4,2), (4,3), (4,4),
(5,1), (5,2), (5,3), (5,4), (5,5),
(6,1), (6,2), (6,3),
(7,2), (7,4), (7,5),
(8,1), (8,4) };
答案 0 :(得分:0)
如果您需要获得导演的姓名,该导演在60分钟内执导了2部或更多电影,每部电影中有3至7名演员参加过(如您在评论中所述),请尝试以下操作:
// use group join to get movies with actors
var moviesWithActors =
from m in movies.Where(x => x.ReleaseDate <= new DateTime(2015, 12, 31) && x.Rating >= 9)
join ma in movieActor on m.MovieId equals ma.MovieId into groupJoin
let actorsInEachMovieCount = groupJoin.Count()
where actorsInEachMovieCount > 2 && actorsInEachMovieCount < 8
select new
{
MovieId = m.MovieId,
};
// the rest of query
var query =
from d in directors
join m in movies on d.Id equals m.DirectorId
join ma in moviesWithActors on m.MovieId equals ma.MovieId // use moviesWithActors from the first query
group new {
Director = d.Name,
Movie = m
} by d.Id into gr
let moviesDurationOver60MinCount = gr.Select(x => x.Movie).Distinct().Count(x => x.DurationMinutes >= 60)
where moviesDurationOver60MinCount >= 2
select new { DirectorName = gr.First().Director };
此外,可以通过以下方式实现相同目的:
// use group join to get movies with duration > 60 min
var filteredMovies =
from d in directors
join m in movies.Where(x => x.DurationMinutes >= 60 && x.ReleaseDate <= new DateTime(2015, 12, 31) && x.Rating >= 9)
on d.Id equals m.DirectorId into groupMovies
let moviesCountForEachDirector = groupMovies.Count()
where moviesCountForEachDirector > 1
select groupMovies;
// use group join to get movies with actors from 3 to 7
var moviesWithActors =
from m in filteredMovies.SelectMany(x => x)
join ma in movieActor on m.MovieId equals ma.MovieId into groupJoin
let actorsInEachMovieCount = groupJoin.Count()
where actorsInEachMovieCount > 2 && actorsInEachMovieCount < 8
select new
{
MovieId = m.MovieId,
};
// the rest of query
var query =
from d in directors
join m in movies on d.Id equals m.DirectorId
join ma in moviesWithActors on m.MovieId equals ma.MovieId
group d by d.Id into gr
select new { DirectorName = gr.First().Name };
希望这会有所帮助。
答案 1 :(得分:0)
最后,我想出了解决方案。 首先,我更改了SQL查询(在SO帮助下):
SELECT Name
FROM (SELECT m.Id, d.Name, COUNT(*) as NumActors
FROM Director d
JOIN Movie m
ON d.Id = m.DirectorId
JOIN MovieActor ma
ON m.Id = ma.MovieId
WHERE m.ReleaseDate <= '2005-12-31' AND
m.Rating >= 9 AND
m.DurationMinutes >= 60
GROUP BY d.Name, m.Id
HAVING COUNT(*) BETWEEN 3 AND 7
) m
GROUP BY Name
HAVING COUNT(*) >= 2;
然后在经过图像修改的linq查询中。
var query4 = (from x in
(from d in directors
join m in movies2
on d.Id equals m.DirectorId
join ma in movieActors
on m.Id equals ma.MovieId
where m.ReleaseDate <= Convert.ToDateTime("2005-12-31")
where m.Rating >= 9
where m.DurationMinutes >= 60
group d by new { d.Name, m.Id } into res
where res.Count() >= 3
where res.Count() <= 7
select res)
group x by x.Key.Name into fin
where fin.Count() >= 2
select fin)
;
答案 2 :(得分:0)
这总是让我感到困惑,如果人们有一个需要使用LINQ解决的要求,他们不会给我们要求,而是先将要求转换为SQL,然后给我们提供SQL而不是要求。
在我看来,您想要一个返回以下内容的LINQ语句:
请提供曾在某部
date
或之前导演过电影的所有导演的姓名,这些导演的评分高于或等于某个评分编号,并且导演了至少两部电影的时长至少一个小时,且至少3个,最多7个演员
如果表之间具有一对多或多对多关系,并且您想要“项目及其许多子项”,例如学校及其学生,客户及其订单,订单及其OrderLines,则您应该考虑使用Enumerable.GroupJoin
另一方面,如果您想要带有子项目的子项目,例如,学生拥有他所参加的唯一学校的学生,或拥有该订单的唯一顾客的订单,使用Enumerable.Join
在这种情况下,我们希望导演与他的电影一起使用,所以我们将使用GroupJoin。
TimeSpan oneHour = TimeSpan.FromHours(1);
var result = directors.GroupJoin(movies,
director => director.Id, // from each director take the Id
movie => movie.DirectorId // from each movie take the DirectorId
// result selector: take every director with all his movies to make one new:
(director, moviesOfThisDirector) => new
{
// from the director we only need his name
Name = director.Name
// From each of his movies we need the ReleaseDate, the Rating and the number of actors
Movies = moviesOfThisDirector.Select(movie => new
{
ReleaseDate = movie.ReleaseDate,
Rating = movie.Rating,
Duration = movie.Duration,
NumberOfActors = movie.GroupJoin(actors,
movie => movie.Id,
actor => actor.MovieId,
// ResultSelector: only count all Actors in this movie
(movie, actorsInThisMovie) => actorInThisMovie.Count(),
})
// well, we don't want all movies,
// we only want movies before a date with a high rating and a certain duration
.Where(movie => movie.ReleaseDate <= myDate
&& movie.Rating >= myRating
&& movie.Duration >= oneHour),
})
// we don't want all Directors, we only want those that have at least one such a movie
.Where(directory => directory.Movies.Any();
可能的改进
您想按名称对董事进行分组。您确定没有同名董事吗?通过ID加入董事会更快,而且您会知道此ID是唯一的。
您似乎在电影和演员之间设计了一对多的关系:每个电影都有零个或多个演员,每个演员只在一部电影中播放。通常,每个电影都有零个或多个演员,每个演员都播放零个或多个电影:多对多关系,需要连接表。考虑您是否真的想要一对多的关系。