【问题标题】:db design: efficiency consideration when adding an intermediate class into a Many-Many relationshipdb design:将中间类添加到多对多关系时的效率考虑
【发布时间】:2011-08-13 22:09:22
【问题描述】:

我知道在这样的情况下,通常会引入一个中级类来捕获信息,例如,一个球队有很多球员,而一名球员多年来为许多球队效力。引入的中间类是具有基数的契约,如下所示:

团队 -1----N- 合同 -N----1- 玩家

但是,假设 98% 的查询只需要当前信息而不关心历史信息。给定一个球员的名字,他们想知道他当前球队的信息,也许还有当前的合同。

鉴于上述关系,是否应该始终查看所有合同以首先找到当前合同,然后从那里访问有关团队的信息?还是应该通过玩家和他的当前团队之间的直接联系进行优化?

谢谢

【问题讨论】:

    标签: database database-design performance many-to-many


    【解决方案1】:

    如果确定在给定时间每个球员只有一支球队,您只需添加 currentTeam 列到 Player 表,就是这样。但请记住,每次更新合同表时都必须更新它!并且必须在事务内完成,以便数据库随时保持一致。

    您以这种方式违反了某些正常形式,但您知道您这样做的目的和原因 - 为了提高效率和优化。这个技巧我做了很多次。

    【讨论】:

    • 谢谢 Tomas,这正是我需要知道的。
    • @Ray,不客气!至于事务,如果您设法在 1 个查询中更新数据库,则不需要使用它。你可以例如run 1 update command for more tables within one query,但是如果你需要运行insertupdate,那么它是2个查询,你应该使用事务。
    【解决方案2】:

    这似乎是在某种 ORM 的背景下,所以我会用它来运行。 (即使不是,请继续阅读。)

    对象对于复杂操作的建模很有用。例如,添加一个新的Contract 会导致TeamPlayers 和各种PayChecks 发生各种疯狂的事情(我做了最后一个,但你明白了) .与在极其复杂的 T-SQL 存储过程中相比,这是在代码中处理的完美类型。

    但是当涉及到查询时,我发现编写一个无耻地定制到一组信息的视图/SQL语句/投影通常是有意义的你需要执行一个功能。只要您这样做是为了读取数据,而不是为了写入数据,那么您并没有真正颠覆您的对象模型;您只是以不同的方式查看,并且您只是在进行务实的观察,大多数时候,您只需要来自 IPlayerCurrentContractQuery 的信息,而不是 @987654327 的整个列表@s 在Player 中。由于它是一种被称为 bajillion times 的方法,因此您编写了一个集成测试以确保 SQL 产生正确的结果,并且您仔细查看了它的查询计划以确保它没有做像 table 这样糟糕的事情扫描到数据库。你的应用中这个常用的屏幕速度很快,每个人都很开心。

    人们可能会认为创建这样一个单独的查询是一种过早的优化,但它可能不是。我的意思是,如果一个玩家通常只有几个Contracts,那么将查询和界面分开可能不值得。从数据库中提取所有Contracts 以循环遍历它们并提取当前的性能会比首先从数据库中选择正确的更差,但如果它只是少数Contracts,那么“是的,我完全意识到这有点愚蠢,但它足够快”的方法可能已经足够好了,继续前进。但是如果这些Contracts 可以追溯到几年前或者是大型对象,那么分离查询就变得很容易了。

    如果那个由于连接而开始表现不佳(除非您开始看到大量流量,否则这不太可能),那么您添加一个缓存。如果 that 由于大量写入而无法正常工作,那么您可以通过添加直接引用来开始对数据库进行非规范化。但除非你正在编写下一个棒球 Facebook,然后是 YAGNI,那时你正在跨服务器进行分片并放弃关系模型的大部分好处,所以谁在乎呢。

    在我对this question 的回答中提出了类似的情况。

    (如果这个问题与 ORM 无关,而实际上只是对表的设计方式进行建模,那么请确保您有一个索引,该索引涵盖了选择当前合约的查询——例如 start 和 stop日期——除非你真的有上面提到的特殊缩放要求,否则你已经完成了。如果你经常编写一组特定的连接,那么你可能会编写一个函数或存储过程来删除样板。)

    那是我的大脑转储。希望这会有所帮助!

    【讨论】:

    • 尼古拉斯 - 感谢您的所有想法。是的,我的“倾斜”是 ORM,特别是 GRAILS GORM。所以,我听到你说的是,如果我“在数据库中”,从优化查询的良好存储过程开始,如果历史确实足够深,甚至需要它。 (总是只优化你需要的东西)。然后,编写一个缓存——我不熟悉——然后,使用直接引用进行非规范化。根据 Tomas 的回答,如果我从添加直接引用开始,我必须采用额外的事务代码。所以,我喜欢你所说的开头:优化你需要的东西。
    • 顺便说一句 - 如果你还没有看过 GORM - 它是多么的简洁,可以用谷歌搜索“Grails refcardz 入门”,并查看“关系”部分,或者更多涉及的 GORMS 手册或免费书籍, Grails 入门。
    【解决方案3】:

    鉴于上述关系,所有的合同都应该是 浏览以首先找到当前的,然后从那里 访问有关团队的信息?

    现代查询优化器将首先使用最具选择性的索引。假设 player_id 在该索引中的可用位置,优化器可能会首先找到该玩家的所有行——不会很多,对吧?——然后在合同日期上进行另一次索引扫描以找到目前的合同。

    如果我是你,我会创建一个只返回“当前”行的视图。让应用程序代码针对该视图运行。

    【讨论】:

    • 我明白了,谢谢。我在“下面”使用 MySql,据我所知,它的查询优化器已打开。然而,我使用 GRAILS ORM (GORM) 的抽象级别更高,它是 hibernate 之上的简化,而不是直接将视图添加到数据库中。但是,感谢您指出这种潜在的优化(一个视图),因为如果需要,我可能会提升两个级别的封面。
    猜你喜欢
    • 2018-08-16
    • 1970-01-01
    • 2018-04-04
    • 2016-05-16
    • 2017-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-15
    相关资源
    最近更新 更多