[英]Summarize hierarchical data with Linq to DB
I have a database that is represented(roughly) by the following structure.我有一个由以下结构(大致)表示的数据库。 Note that since Entity Framework generates it, the GamePlayer class has the reference to a parent Game instance and GameAction class has the reference to a parent GamePlayer instance and corresponding Ids.
请注意,由于 Entity Framework 生成它,GamePlayer class 具有对父 Game 实例的引用,而 GameAction class 具有对父 GamePlayer 实例和相应 Id 的引用。
public class Player
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Game
{
public int Id { get; set; }
public IEnumerable<GamePlayer> Players { get; private set; }
}
public class GamePlayer
{
public int Id { get; set; }
public int PlayerId{get;set;}
public int GameId { get; set; }
public Game Game { get; set; }
public IEnumerable<GameAction> Actions { get; private set; }
}
public class GameAction
{
public int Id { get; set; }
public ActionType ActionType { get; set; }
public int? Amount { get; set; }
public int GamePlayerId { get; set; }
public GamePlayer GamePlayer { get; set; }
}
public enum ActionType
{
BET,
FOLD,
RAISE
}
A single player can play multiple games and perform various actions.一个玩家可以玩多个游戏并执行各种动作。
I want to summarize the data to get the following statistic for each player:我想汇总数据以获得每个玩家的以下统计数据:
For example:例如:
Player Name![]() |
Games![]() |
Bets![]() |
Amount![]() |
---|---|---|---|
John![]() |
120 ![]() |
20 ![]() |
980 ![]() |
Paul![]() |
90 ![]() |
10 ![]() |
5 ![]() |
Garry![]() |
200 ![]() |
100 ![]() |
1500 ![]() |
The database contains thousands of players, millions of games, and even more actions.该数据库包含成千上万的玩家、数百万的游戏,甚至更多的动作。 Therefore, ideally, I would like to write a Linq request that runs on the server-side to prevent all the records from being loaded to the client.
因此,理想情况下,我想编写一个在服务器端运行的 Linq 请求,以防止将所有记录加载到客户端。
Is it feasible?可行吗? Or should I resort to stored procedures and pure SQL queries on the DB level?
还是我应该在数据库级别使用存储过程和纯 SQL 查询?
The desired LINQ query could be written in two ways - top-down without GroupBy
, just following the naturally grouped data Player
-> GamePlayer
-> GameAction
, or bottom-up GameAction
-> GamePlayer
and GroupBy
(the traditional SQL way).所需的 LINQ 查询可以用两种方式编写 - 自上而下没有
GroupBy
,只遵循自然分组的数据Player
-> GamePlayer
-> GameAction
,或自下而上GameAction
-> GamePlayer
和GroupBy
(传统的 SQL 方式)。
The first approach would generate many correlated subqueries (server side, but still), so probably it is better to follow the SQL way.第一种方法会生成许多相关的子查询(服务器端,但仍然如此),所以可能最好遵循 SQL 方式。 Moreover EF Core 6.0 supports distinct count translation which is needed here to count the games duplicated by actions.
此外,EF Core 6.0 支持不同的计数转换,这里需要计算由动作重复的游戏。
Here is the query which returns the data you want by PlayerId
.这是通过
PlayerId
返回您想要的数据的查询。 You can always join it to Player
in case you need other player info.如果您需要其他播放器信息,您可以随时将其加入
Player
器。
var query =
from gp in db.Set<GamePlayer>()
from ga in gp.Actions
group new { gp, ga } by new { gp.PlayerId } into g
select new
{
PlayerId = g.Key.PlayerId,
Games = g.Select(e => e.gp.GameId).Distinct().Count(),
Bets = g.Count(e => e.ga.ActionType == ActionType.BET),
Amount = g.Sum(e => e.ga.ActionType == ActionType.BET ? e.ga.Amount : null),
};
which for SqlServer is translated as这对于 SqlServer 被翻译为
SELECT [g].[PlayerId], COUNT(DISTINCT ([g].[GameId])) AS [Games], COUNT(CASE
WHEN [g0].[ActionType] = 0 THEN 1
END) AS [Bets], COALESCE(SUM(CASE
WHEN [g0].[ActionType] = 0 THEN [g0].[Amount]
ELSE NULL
END), 0) AS [Amount]
FROM [GamePlayer] AS [g]
INNER JOIN [GameAction] AS [g0] ON [g].[Id] = [g0].[GamePlayerId]
GROUP BY [g].[PlayerId]
which is very close to the what one would write in SQL.这与 SQL 中的内容非常接近。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.