我在Windows机器上使用PostgreSQL 9.3。 假设我有一个包含400万条记录的客户表和一个包含2500万条记录的订单表。 但是,我只对我的纽约客户感兴趣。只有5,000名纽约客户订购了15,000个订单,即一个非常小的子集。
检索客户ID的最佳方法是什么,以及纽约客户的订单总数是多少?
是一个相关的子查询,如
Select c.clientid
, ( select count(orders.clientid) from orders where orders.clientid = c.clientid) as NumOrders
From clients c
WHERE c.city = 'New York'
比像
这样的连接更快Select c.clientid
,coalesce(o.NumOrders,0) as NumOrders
From clients c
Left outer join
( select clientid, count(*) as NumOrders from orders group by clientid ) o
on c.clientid = o.clientid
WHERE c.city = 'New York'
因为后者花费大部分时间来计算记录,然后因为它们与纽约客户无关而被丢弃? 或者有更好的方法吗?
谢谢!
PS是的,我知道,我应该看一下执行计划,但我是在家写这个,而且我没有一个包含数百万条记录的数据库来测试它。答案 0 :(得分:2)
正如您所提到的,真正知道的唯一方法是比较执行计划。实际上,最好的方法是使用EXPLAIN ANALYZE
,以便它实际执行查询并将结果插入带有估计值的输出中,这样您就可以了解查询计划程序与现实。
但是,一般来说,在这种情况下我会做的可能是为客户端子集创建临时表,然后为JOIN
创建orders
表。您可以选择使用WITH
代替一次查询。
所以,比如:
CREATE TEMP TABLE tmp_clients AS
SELECT c.clientid
FROM clients c
WHERE c.city = 'New York'
ORDER BY c.clientid;
SELECT *
FROM orders AS o
JOIN tmp_clients AS c ON (o.clientid = c.clientid)
ORDER BY o.clientid;
这样,tmp_clients
只包含纽约客户 - 约5K行 - 而且该表将加入订单表。
您还可以进一步优化,在临时表(在clientid上)创建一个索引,然后在ANALYZE
之前JOIN
创建一个索引,以确保JOIN完全在索引上完成。您需要检查每种情况下的查询计划以查看相对差异(或者只要记住JOIN
并不如您所希望的那么快)。
对@poshest评论的回复:
听起来像临时表正在堆叠,这会增加内存占用,而且对于长时间运行的连接,功能似乎是内存泄漏。
在这种情况下,它不会是真正的泄漏,因为临时表的作用域是连接。它们会自动消失,但直到连接结束后才会消失。但是,当你完成它们时,你可以让它们立即消失。简单DROP
表格就像你完成任何其他表格一样,我怀疑你能够多次调用这个函数 - 在同一个连接上 - 没有相同的类型单调内存占用增加。
答案 1 :(得分:0)
在第一种情况下,您使用的子查询必须每个匹配的客户端ID号至少执行一次。它将表现不佳。
在第二种情况下,您在子查询上使用左外连接,这是 little 奇数。它将为没有订单的客户保留价值。这有点像谈论从未购买任何东西的顾客 - 他们真的是顾客吗?我要说“不”,因为这是我的答案,我可以弥补我喜欢的任何东西。但我已经看到了 有意义地讨论你从未为之工作的客户的情况。 (律师事务所有时会让客户没有收费时间。)
无论如何。 。
通过简单,直接地表达您想要计算的内容,您几乎肯定会更好。
select o.clientid, count(*)
from orders o
inner join clients c on c.clientid = o.clientid
and c.city = 'New York'
group by o.clientid;
你需要一个关于“城市”的索引。
我使用随机(ish)数据构建,填充和索引了一些表。简单直接的查询速度提高了一个数量级。
explain analyze
Select c.clientid
,coalesce(o.NumOrders,0) as NumOrders
From clients c
Left outer join
( select clientid, count(*) as NumOrders from orders group by clientid ) o
on c.clientid = o.clientid
WHERE c.city = 'New York';
"Merge Right Join (cost=4813633.08..5022834.83 rows=5200 width=12) (actual time=105751.121..117901.326 rows=5000 loops=1)" " Merge Cond: (orders.clientid = c.clientid)" " -> GroupAggregate (cost=4799762.91..4996891.51 rows=962770 width=4) (actual time=105702.262..117735.305 rows=1000000 loops=1)" " -> Sort (cost=4799762.91..4862263.21 rows=25000120 width=4) (actual time=105688.877..113148.881 rows=25000000 loops=1)" " Sort Key: orders.clientid" " Sort Method: external merge Disk: 342176kB" " -> Seq Scan on orders (cost=0.00..360621.20 rows=25000120 width=4) (actual time=14.866..35814.499 rows=25000000 loops=1)" " -> Sort (cost=13870.18..13883.18 rows=5200 width=4) (actual time=39.536..40.241 rows=5000 loops=1)" " Sort Key: c.clientid" " Sort Method: quicksort Memory: 427kB" " -> Bitmap Heap Scan on clients c (cost=144.73..13549.22 rows=5200 width=4) (actual time=29.556..30.638 rows=5000 loops=1)" " Recheck Cond: ((city)::text = 'New York'::text)" " -> Bitmap Index Scan on clients_city_idx (cost=0.00..143.43 rows=5200 width=0) (actual time=29.538..29.538 rows=5000 loops=1)" " Index Cond: ((city)::text = 'New York'::text)" "Total runtime: 118027.256 ms"
explain analyze
select o.clientid, count(*)
from orders o
inner join clients c on c.clientid = o.clientid
and c.city = 'New York'
group by o.clientid;
"GroupAggregate (cost=595747.05..596315.80 rows=32500 width=4) (actual time=9167.518..9179.855 rows=1250 loops=1)" " -> Sort (cost=595747.05..595828.30 rows=32500 width=4) (actual time=9167.504..9174.135 rows=31336 loops=1)" " Sort Key: o.clientid" " Sort Method: external merge Disk: 432kB" " -> Hash Join (cost=13614.22..593311.47 rows=32500 width=4) (actual time=3.055..9123.568 rows=31336 loops=1)" " Hash Cond: (o.clientid = c.clientid)" " -> Seq Scan on orders o (cost=0.00..360621.20 rows=25000120 width=4) (actual time=0.041..3833.387 rows=25000000 loops=1)" " -> Hash (cost=13549.22..13549.22 rows=5200 width=4) (actual time=2.915..2.915 rows=5000 loops=1)" " Buckets: 1024 Batches: 1 Memory Usage: 176kB" " -> Bitmap Heap Scan on clients c (cost=144.73..13549.22 rows=5200 width=4) (actual time=0.672..1.769 rows=5000 loops=1)" " Recheck Cond: ((city)::text = 'New York'::text)" " -> Bitmap Index Scan on clients_city_idx (cost=0.00..143.43 rows=5200 width=0) (actual time=0.658..0.658 rows=5000 loops=1)" " Index Cond: ((city)::text = 'New York'::text)" "Total runtime: 9180.291 ms"