针对多对一关系优化 SQL 查询

Posted

技术标签:

【中文标题】针对多对一关系优化 SQL 查询【英文标题】:Optimizing a SQL Query for a Many to One Relationship 【发布时间】:2011-09-23 14:16:57 【问题描述】:

我有两个具有多对一关系的表,我将它们称为 Parent_Table 和 Child_Table(即父母有零个或多个孩子,但孩子只有一个父母)。我需要计算至少有一个孩子满足某些条件的父母的数量。哪个查询是最优的?

选项1(很确定不是这个)

SELECT COUNT(DISTINCT(pt.ID)) 
FROM PARENT_TABLE pt
JOIN CHILD_TABLE ct
ON pt.ID =  ct.PARENT_ID
WHERE <parent meets some condition>
AND <child meets some condition>

选项 2

SELECT COUNT(pt.ID)
FROM PARENT_TABLE pt
WHERE pt.ID in
(
SELECT ct.PARENT_ID
FROM CHILD_TABLE ct
WHERE <child meets condition>
)
AND <parent meets some condition>

选项 3(我猜是最快的)

SELECT COUNT(pt.ID)
FROM PARENT_TABLE pt
WHERE EXISTS
(
SELECT 1
FROM CHILD_TABLE ct
WHERE ct.PARENT_ID = pt.ID
AND <child meets condition>
)
AND <parent meets some condition>

或者完全是别的什么?是取决于每张表的大小,还是两个条件的复杂程度,还是数据是否排序?

编辑:数据库是 Oracle。

【问题讨论】:

为什么不对一些适当大小的样本数据做一些性能测试? 在 SQL Server 2005 或更高版本中,选项 2 和 3 的处理方式相同。 要获得准确的答案,请分享您的 RDBMS。 在Oracle中依赖于统计,但是2和3可能是一样的。 猜测一文不值,你应该在足够大的样本数据上查看实际的查询执行计划。注意索引使用情况以及实际获取的行数和估计的行数之间的不匹配(大的不匹配表明您需要在表上创建/更新统计信息)。 【参考方案1】:

第一个查询很慢,其他查询应该在大多数数据库上运行得很快。

不了解数据库很难说更多:

但是:count(*) 通常比 count(names_field) 快 而且永远不会慢 count(distinct (afield)) 很慢

或者完全是别的什么?

这取决于数据库和数据库的确切版本。

是否取决于每个表的大小

是的,这很重要

或两个条件的复杂度

有可能

或者数据是否排序?

如果您想要快速选择,则必须为用于连接的所有字段编制索引。 并且 where 子句中使用的所有字段都必须是索引的或低基数的。

【讨论】:

+1 - 使用有限的信息,这几乎可以概括。 @EvilTeach,不在内部联接上下文中的主键上。 啊是的。这是一个一般性警告。【参考方案2】:

对我来说,第一个似乎是最好的,因为它最容易阅读,但这显然不能回答你的问题。

您真正需要做的是为每个查询生成执行计划并对其进行分析(我认为大多数流行的 DBMS 都有一个工具可以做到这一点)。它将为您提供每个查询的成本值。

如果你不能这样做,我猜你可以多次运行查询并比较执行时间。

或者完全是别的什么?是取决于每张表的大小,还是两个条件的复杂程度,还是数据是否排序?

所有这些以及更多。

【讨论】:

【参考方案3】:

正如评论者所说,回答这个问题的最佳方法是运行查询和测量。

但是,一般而言,数据库引擎非常非常有效地优化连接 - 我很确定您会发现这 3 个查询之间几乎没有区别,而且查询优化器完全有可能将它们全部转换为相同的基本查询( 2 和 3 是等价的)。

到目前为止,对查询影响最大的将是“孩子满足某些条件”和“父母满足某些条件”子句。我会专注于优化这一点。

【讨论】:

不正确。 IN vs DISTINCT 将对处理时间产生非常很大的影响。 啊是的 - DISTINCT 通常是邪恶的,尤其是。从性能的角度来看。没有发现那个小块……但是,我确实认为 where 子句会产生很大的影响

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

没有外键关系实例的 Django 查询多对一关系

Hibernate 多对一关联查询

flask 定义数据关系(多对一)

Hibernate中一对多和多对一关系

休眠:多对一关系失败

Mybatis_多对一关联查询