优化大数据集上的多对多关系查询

Posted

技术标签:

【中文标题】优化大数据集上的多对多关系查询【英文标题】:Optimizing query with many-to-many relationship on big data set 【发布时间】:2013-07-03 16:39:15 【问题描述】:

我有一个使用类似 DDL 构建的数据库 (SQLite):

                                    CREATE TABLE [Player] (
                                        [PlayerID] INTEGER  PRIMARY KEY AUTOINCREMENT NOT NULL,
                                        [Name] TEXT  UNIQUE NULL
                                        );
                                    CREATE TABLE [Position] (
                                        [PlayerID] INTEGER  NOT NULL,
                                        [SingleHandID] INTEGER  NOT NULL,
                                        [Position] INTEGER  NULL,
                                        PRIMARY KEY ([PlayerID],[SingleHandID])
                                        );
                                    CREATE TABLE [SingleHand] (
                                        [SingleHandID] INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,
                                        [Stake] FLOAT  NULL,
                                        [Date] DATE  NULL,
                                        DataSetID INTEGER NULL
                                        [IsPreflopAllIn] BOOLEAN  NULL
                                        );

                                    CREATE UNIQUE INDEX [NameIndex] ON [Player](
                                        [Name]  ASC

                                    CREATE INDEX [DataSetIndex] ON [SingleHand](
                                    [DataSetID]  ASC
                                    );

它被映射到实体框架模型。我正在处理每个多达 1000 万条记录的大型数据集。

我的问题是,我需要找到特定玩家在任何给定位置上刺痛的所有手牌(加上一些其他过滤器,例如日期范围)。

虽然我可以非常快速地扫描数据库,以从单个表中查找数据,例如:

//[playerIDs and selectedPos are cashed in memory]

context.Positions.Where(p => playerIDs.Contains(p.PlayerID) && selectedPos.Contains(p.Position)).Select(p => p.SingleHandID).Take(maxHands ?? 1);

当我需要在表之间进行任何连接时,它开始运行很慢,例如:

//accesing both Position and SingleHand table
context.Positions.Where(p => playerIDs.Contains(p.PlayerID) && selectedPos.Contains(p.Position) && p.SingleHand.DataSetID == dataSetNumber).Select(p => p.SingleHandID).Take(maxHands ?? 1);

我可以提取什么巧妙的技巧、合并查询和代码(例如,使用本地缓存),以使其运行效率最高?我正在使用 System.Data.SQLite 提供程序。

也许我应该在 Position 表中添加多余的 DataSetID,然后我只能在 Position 表上进行主要查询?稍后,当我将获得所有匹配手的 ID 时,添加附加条件(如日期检查)应该会更快

【问题讨论】:

有太多变量会影响您建议的超过 10m 记录的查询的性能。表行大小、SQL 索引和一堆设置。对于粗略的第一次拍摄,我建议创建一个视图,在您的数据库服务器中单独优化它,然后使用 EF 来查询它 NameIndex 是多余的,因为该列已经具有 UNIQUE 约束。 要检查查询的优化程度,将其转换为 SQL 并查看 EXPLAIN QUERY PLAN 的输出。 【参考方案1】:

创建新索引:

CREATE INDEX [DataSetIndex2] ON [SingleHand](
    [SingleHandID] ASC,
    [DataSetID]  ASC
);

这应该很有帮助。

你也可以试试这样的:

context.Positions
  .Where(p => playerIDs.Contains(p.PlayerID) && SelectedPos.Contains(p.Position))
  .Select(p => p.SingleHandID)
.Intersect(context.SingleHand
   .Where(s=>s.DataSetId==dataSetNumber)
   .Select(s=>s.SingleHandID))
.Take(maxHands ?? 1);

【讨论】:

非常感谢 :) 我需要做更多的测试,但看起来,使用该索引,性能是可以接受的(使用我发布的代码,尝试你的 Intersect 似乎给出了磨损的结果这个案例)。我还有一个问题 - 当我将“.OrderBy(p => p.SingleHandID)”添加到查询中时,我可以使用使用分页(在用户等待执行此操作时向用户提供有关进度的一些反馈)它速度非常慢 - 可能我没有使用任何索引。添加以下索引是个好主意吗? INDEX [HandIdIndex] ON [Position]( [SingleHandID] ASC ) 可能。或者您可以更改复合主键以将 SingleHandID 列为第一个键。性能提升不会那么大,因为您仍然需要查找记录来过滤它,但它可能会有所帮助。

以上是关于优化大数据集上的多对多关系查询的主要内容,如果未能解决你的问题,请参考以下文章

核心数据:与状态的多对多关系

针对标签上的多对多连接优化 MySQL 查询

在 TypeORM 与 GraphQL 的多对多关系上使用数据加载器,查询多对多

针对特定集合的多对多关系核心数据查询

父/子表单上的多对多关系限制了数据输入

在 Laravel 中查询用户的多对多关系