同一个窗口函数查询,只改外层ORDER BY的写法,看执行计划里Sort出现几次,以及'看起来一样'的排序条件是否真的能省掉一次排序。
在5万行orders上按customer_id分组,用created_at排序算累计金额,只改最外层的ORDER BY子句。
代码SELECT customer_id, created_at, amount, SUM(amount) OVER (PARTITION BY customer_id ORDER BY created_at) AS running_total FROM orders -- 分别测试:不加ORDER BY / ORDER BY customer_id, created_at(和窗口子句完全一致)/ ORDER BY amount DESC(不一致)
-> Window aggregate with buffering: sum(orders.amount) OVER (PARTITION BY orders.customer_id ORDER BY orders.created_at)
-> Sort: orders.customer_id, orders.created_at ← 只有这一次排序
-> Index range scan on orders using idx_cust ...窗口函数要按PARTITION BY customer_id ORDER BY created_at计算累计值,必须先把数据按这个顺序排好,这一次排序无法省略。这时候整条查询里只有这一次Sort。
-> Sort: orders.customer_id, orders.created_at ← 第二次排序,和窗口内部那次条件完全一样
-> Table scan on <temporary>
-> Temporary table
-> Window aggregate with buffering: sum(...) OVER (...)
-> Sort: orders.customer_id, orders.created_at ← 第一次排序(窗口自己需要的)
-> Index range scan on orders using idx_cust ...不加ORDER BY: 总耗时约 92.3 ms(只排1次) ORDER BY customer_id, created_at(一样): 总耗时约 96.3 ms(排2次,但都很快) ORDER BY amount DESC(真不一样): 总耗时约 197 ms(排2次,第二次要重新按amount全表排序)
结构上,"一样"的ORDER BY和"真不一样"的ORDER BY都要走两次Sort,属于同一类执行计划;但真实耗时上,"一样"的ORDER BY(96.3ms)反而更接近不排序的基线(92.3ms),因为它的第二次排序处理的是窗口函数已经按同样顺序整理过的数据,排序开销很小;而真不一样的ORDER BY(197ms)第二次要按一个全新的列(amount)对全表重新排序,真实多花了一倍还多的时间。也就是说:即使执行计划的"形状"一样(都是两次Sort),耗时可以差出一倍以上,看执行计划的步骤数量不能代替看真实的EXPLAIN ANALYZE耗时。
每一步都是本文上面用EXPLAIN FORMAT=TREE和EXPLAIN ANALYZE跑出来的原始结果。
EXPLAIN FORMAT=TREE:确认Sort出现的次数和位置EXPLAIN ANALYZE:对比三种ORDER BY写法的真实耗时