为什么规划器不首先执行参与WHERE子句的连接?

时间:2014-11-27 16:32:45

标签: sql postgresql query-optimization xpo

我正在试验PostgreSQL(v9.3)。我有一个非常大的数据库,我经常需要用8-10个连接表(作为大数据网格源)执行查询。我使用Devexpress XPO作为PostgreSQL之上的ORM,所以不幸的是我无法控制如何生成联接。

以下示例相当简单,真实场景更复杂,但就我的考试而言,主要问题也可以在此看到。

考虑(语义上)相同查询的以下变体:

SELECT o.*, c.*, od.* 
FROM orders o 
LEFT JOIN orderdetails od ON o.details = od.oid
LEFT JOIN customers c ON o.customer = c.oid
WHERE c.code = 32435 and o.date > '2012-01-01';

SELECT o.*, c.*, od.* 
FROM orders o 
LEFT JOIN customers c ON o.customer = c.oid
LEFT JOIN orderdetails od ON o.details = od.oid
WHERE c.code = 32435 and o.date > '2012-01-01';

orders表包含约1百万行,customers表约3万行。由于一对一的关系,订单详细信息包含与orders相同的金额。

更新: 看起来这个例子太简单了,无法重现这个问题,因为我再次检查过,在这种情况下,两个执行原则是相同的。然而,在我真正的查询中,有更多的连接,问题出现了:如果我把客户作为第一个连接,执行速度要快100倍。我将添加我的真实查询,但是由于匈牙利语以及它由XPO和Npgsql生成的事实使得它的可读性降低。

第一个查询比第二个查询慢得多(大约100倍),当我用EXPLAIN ANALYZE输出计划时,我可以看到连接的顺序反映了它们在查询字符串中的位置。首先是两个"巨人"表连接在一起,然后连接过滤后的客户表(过滤器只选择一行)。

第二个查询更快,因为连接从该一个客户行开始,之后它加入了20-30个订单详细信息行。

不幸的是,在我的情况下,XPO会生成第一个版本,因此我会遇到性能问题。

为什么PostgreSQL查询规划器没有注意到客户的连接在WHERE clauuse中有条件? IMO正确的优化是首先获取具有任何类型过滤器的连接,然后获取仅参与选择的连接。

感谢任何形式的帮助或建议。

1 个答案:

答案 0 :(得分:1)

如果您的查询加入collapsed,加入订单很重要。这是由查询计划程序在内部完成的,但您可以使用join_collapse_limit运行时选项来操作该进程。

但请注意,默认情况下,query planner每次都无法找到最佳连接顺序:

  

以这种方式约束计划者的搜索是一种有用的技术,既可以减少计划时间,也可以指导计划者获得良好的查询计划。如果计划程序默认选择错误的连接顺序,您可以强制它通过JOIN语法选择更好的顺序 - 假设您知道更好的顺序,即。 建议进行实验。

为了获得最佳性能,我建议使用某种本机查询(如果可用)。提升join_collapse_limit 可以是一个足够好的解决方案,如果你确定,这还没有引起其他问题。

另外值得一提的是,提高join_collapse_limit很可能会增加计划时间。