为了简化事情,我的情况是两个表具有一对多(或在某些情况下可能是一对一)关系:例如已完成的帐户和付款,客户和订单等。每个付款/订单只能与1个帐户相关联,但帐户可能有0个,1个或多个与之相关的付款。 如果我想计算每个帐户/客户的付款/订单总数,我会写这样的内容:
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
但是,我也看到过这种类型的编码:
Select c.clientid
, ( select count(orders.clientid) from orders where orders.clientid = c.clientid) as NumOrders
From clients c
您对哪种方法更可取以及为什么有任何意见?
我觉得后者更难阅读,但也许这只是我的习惯。至于性能,如果我没有where子句,似乎第一个更快,但如果我有一个where子句(例如,一个条件只返回220万条记录客户表中的1,000条记录),那么后者似乎更快。
我使用PostgreSQL 9.1和Microsoft SQL Server 2014.谢谢!
答案 0 :(得分:2)
我更喜欢
Select c.clientid
,count(o.clientid) as NumOrders
From clients c
Left outer join orders o on c.clientid = o.clientid
group by c.clientid
因为它简单明了。
如果我愿意在你的两个版本之间做出选择,我宁愿选择第二个版本,因为它更短(需要阅读和尝试理解的代码更少),但不是特别棘手。第一个必须处理NULL处理,这使得事情变得更加复杂。
答案 1 :(得分:1)
此版本:
Select c.clientid,
( select count(o.clientid) from orders o where o.clientid = c.clientid
) as NumOrders
From clients c;
有一个主要优势。以下几点解释了它:
select c.*, . . .
也就是说,您可以选择自己喜欢的列,而不必将它们放入group by
子句中。提醒一下,您无法将*
放入group by
。
在您的情况下,SQL Server和Postgres具有相当不错的优化器,因此要么应该能够利用索引。并非所有SQL引擎都如此智能。特别是MySQL在第一种情况下使用orders
上的索引比在group by
情况下更好。
也就是说,第二个版本是标准的SQL代码。
答案 2 :(得分:1)
左派连接到派生表而不是select子句中的相关子查询通常会更有效。相关子查询强制循环子查询,而左连接可以使用循环或散列连接。您确实希望在示例中包含该组,就像您在示例中一样,因为它可以使用外键上的索引来计算聚合,而仅对简单的左连接进行分组则不会。如果你有一个where子句,它取决于它过滤哪个表。如果要对orders表进行过滤,请确保在派生表中包含where子句。如果您使用where子句显着地减少了clients表中的行数,那么是的,像第二个示例那样的相关子查询只会执行一些循环子查询,而不是尝试计算整个聚合总计表,可能是数百万的订单。但是,我建议在该实例中使用外部apply来在join子句中而不是在select子句中执行相关子查询,因为如果需要,它将允许您访问表中的其他列,并且没有真正的缺点。所以我一般会推荐你的第一个例子:
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