PostgreSQL中如何通过多表连接实现按国家统计各流派的租赁次数?
最优实现方案:统计各国各流派的租赁次数
嘿,你的思路方向完全没问题——通过关联依赖表后分组统计就是核心解法,咱们先把逻辑捋顺,再聊聊你担心的「9表连接是否明智」这个问题。
先理清表的关联链路
咱们先把需要用到的表和关联关系拆解开,避免冗余连接:
- 从租赁记录到国家的链路:
rental→customer(通过customer_id)→address(通过address_id)→city(通过city_id)→country(通过country_id) - 从租赁记录到流派的链路:
rental→inventory(通过inventory_id)→film(通过film_id)→film_category(通过film_id)→category(通过category_id)
没错,算下来确实是9张表,但只要连接逻辑正确,PostgreSQL处理这种量级的连接完全hold住——尤其是示例数据库里的外键默认都建了索引,性能不会有明显问题。
最终SQL实现(带注释)
下面是满足需求的最优查询语句,每一步都标了用途:
SELECT c.name AS category_name, -- 流派名称 co.country AS country_name, -- 国家名称 COUNT(r.rental_id) AS num_rentals -- 用rental_id统计,避免把NULL算入 FROM rental r -- 关联客户表,拿到客户的地址ID INNER JOIN customer cu ON r.customer_id = cu.customer_id -- 关联地址表,拿到城市ID INNER JOIN address a ON cu.address_id = a.address_id -- 关联城市表,拿到国家ID INNER JOIN city ci ON a.city_id = ci.city_id -- 关联国家表,拿到国家名称 INNER JOIN country co ON ci.country_id = co.country_id -- 关联库存表,拿到对应影片ID INNER JOIN inventory i ON r.inventory_id = i.inventory_id -- 关联影片表,拿到影片的分类关联ID INNER JOIN film f ON i.film_id = f.film_id -- 关联影片分类中间表,拿到流派ID INNER JOIN film_category fc ON f.film_id = fc.film_id -- 关联流派表,拿到流派名称 INNER JOIN category c ON fc.category_id = c.category_id -- 按流派+国家两个维度分组统计 GROUP BY c.name, co.country -- 可选:按租赁次数降序,方便看热门组合 ORDER BY num_rentals DESC;
关键细节说明
- 连接类型选择:这里用
INNER JOIN只会统计有实际租赁记录的国家+流派组合。如果需要覆盖「没有任何租赁的国家」或「从未被租赁的流派」,可以把对应位置的INNER JOIN改成LEFT JOIN,同时注意COUNT要保留r.rental_id(因为LEFT JOIN会产生NULL,COUNT(*)会把NULL也算进去,而COUNT(r.rental_id)只会统计有租赁记录的行)。 - 分组规则:必须把
category.name和country.country都放进GROUP BY里,因为我们要按这两个维度做聚合统计。 - 性能优化:如果查询速度慢,可以检查这些外键字段的索引(示例数据库默认已建,但自定义库需要确认):
rental.customer_id、rental.inventory_id、customer.address_id、address.city_id、city.country_id、inventory.film_id、film_category.film_id、film_category.category_id——索引能大幅提升多表连接的效率。 - 关于「9表连接是否明智」:只要是业务逻辑必需的连接,就完全没问题。PostgreSQL的查询优化器对这种链式连接的处理很成熟,不会因为表多就崩盘。反而把逻辑整合到一个查询里,比多次查询后在应用层聚合更高效,减少了数据库和应用之间的数据传输。
可选变体:统计所有国家(含无租赁的)
如果需要包含那些没有任何租赁记录的国家,可以调整连接顺序,从country开始做左连接:
SELECT c.name AS category_name, co.country AS country_name, COUNT(r.rental_id) AS num_rentals FROM country co LEFT JOIN city ci ON co.country_id = ci.country_id LEFT JOIN address a ON ci.city_id = a.city_id LEFT JOIN customer cu ON a.address_id = cu.address_id LEFT JOIN rental r ON cu.customer_id = r.customer_id LEFT JOIN inventory i ON r.inventory_id = i.inventory_id LEFT JOIN film f ON i.film_id = f.film_id LEFT JOIN film_category fc ON f.film_id = fc.film_id LEFT JOIN category c ON fc.category_id = c.category_id GROUP BY c.name, co.country ORDER BY num_rentals DESC;
这种情况可能会出现category_name为NULL的行(对应没有任何影片租赁的国家),如果需要过滤掉,可以加WHERE c.name IS NOT NULL。
内容的提问来源于stack exchange,提问作者WHOATEMYNOODLES
相关产品推荐
相关产品推荐

