简体   繁体   English

用 Linq 将分层数据汇总到 DB

[英]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:我想汇总数据以获得每个玩家的以下统计数据:

  • Number of games played玩过的游戏数
  • number of bets made投注次数
  • the sum of all bets made所有投注的总和

For example:例如:

Player Name选手姓名 Games游戏 Bets投注 Amount数量
John约翰 120 120 20 20 980 980
Paul保罗 90 90 10 10 5 5
Garry加里 200 200 100 100 1500 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 -> GamePlayerGroupBy (传统的 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM