假设我有四张桌子:
CREATE TABLE dealers (
id SERIAL PRIMARY KEY,
name TEXT UNIQUE NOT NULL
)
CREATE TABLE brands (
id SERIAL PRIMARY KEY,
name TEXT UNIQUE NOT NULL
)
CREATE TABLE cars (
id SERIAL PRIMARY KEY,
brand INTEGER REFERENCES (brands.id) NOT NULL,
name TEXT UNIQUE NOT NULL
)
CREATE TABLE sells (
id SERIAL,
dealer INTEGER REFERENCES (dealers.id) NOT NULL,
car INTEGER REFERENCES (cars.id) NOT NULL
)
如果n是经销商的数量和经销商数量的m,如何在sql中创建一个n x m矩阵,显示经销商销售的品牌汽车数量。
输出应该如下所示:
Dealer |Honda Toyota Tesla
CarMax 103 204 1
CheapCars 160 320 0
GoodCars 40 20 2
OCHonda 201 0 0
我可以用来完成此任务的最简单,最有效的查询是什么?
编辑:将m更改为经销商而非汽车数
答案 0 :(得分:2)
SELECT d.name AS dealer, b.name AS brand, sells
FROM (
SELECT s.dealer, c.brand, count(*)::int AS sells
FROM sells s
JOIN cars c ON c.id = s.car
GROUP BY 1,2
) x
JOIN brands b ON b.id = x.brand
JOIN dealers d ON d.id = x.dealer
ORDER BY 1,2;
先分组,稍后加入详情,通常会更快
如果你想要number of dealers instead of cars
,就像你以后编辑的那样,只需使用:
count (DISTINCT s.dealer)
代替count(*)
。
从另外的Postgres模块tablefunc
将其提供给crosstab()
通常,您需要明确列出所有品牌,因为SQL需要提前知道列。
SELECT * FROM crosstab(
'SELECT d.name AS dealer, b.name AS brand, sells
FROM (
SELECT s.dealer, c.brand, count(*)::int AS sells
FROM sells s
JOIN cars c ON c.id = s.car
GROUP BY 1,2
) x
JOIN brands b ON b.id = x.brand
JOIN dealers d ON d.id = x.dealer
ORDER BY 1,2'
,$$VALUES ('Honda'::text), ('Toyota'), ('Tesla')$$ -- add more ...
) AS t(dealer text, "Honda" int, "Toyota" int, "Tesla" int); -- add more ...
在这个相关答案中有充分的细节和解释:
PostgreSQL Crosstab Query
答案 1 :(得分:1)
假设您知道品牌数量,那么您可以count
使用case
:
select d.name as dealer,
count(case when b.name = 'Honda' then 1 end) as HondaCount,
count(case when b.name = 'Toyota' then 1 end) as ToyotaCount,
count(case when b.name = 'Telsa' then 1 end) as TelsaCount
from sells s
join dealers d on s.dealer = d.id
join cars c on s.car = c.id
join brands b on c.brand = b.id
group by d.name
如果有经销商没有任何销售,您需要将其包括在结果中,那么从经销商表开始使用outer join
:
...
from dealers d
left join sells s on s.dealer = d.id
left join cars c on s.car = c.id
left join brands b on c.brand = b.id
group by d.name
答案 2 :(得分:1)
我想建议在SQL中,你真的不想要一个矩阵。你想要一对经销商和品牌以及数量。你可以这样做:
select d.name as dealername, b.name as brandname, count(s.id) as numsales
from brands b cross join
dealers d left outer join
cars c
on c.brand = b.id left outer join
sells s
on s.dealer = d.id and s.car = c.id
group by d.name, b.name;
如果您确实希望每个经销商占一行,那么您需要转动数据。如果你了解这些品牌,你可以这样做:
select d.name as dealername,
sum(case when b.name = 'Honda' and s.id is not null then 1 else 0 end) as Honda,
sum(case when b.name = 'Toyota' and s.id is not null then 1 else 0 end) as Toyota,
sum(case when b.name = 'Tesla' and s.id is not null then 1 else 0 end) as Tesla
from brands b cross join
dealers d left outer join
cars c
on c.brand = b.id left outer join
sells s
on s.dealer = d.id and s.car = c.id
group by d.name, b.name;
如果你明确知道你想要的品牌,这是有效的。如果您不了解品牌,则必须采用动态支点。常规SQL查询返回一组固定名称的固定列。