如何从SQL中的两个表创建矩阵?

时间:2014-05-26 01:59:49

标签: sql postgresql matrix

假设我有四张桌子:

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更改为经销商而非汽车数

3 个答案:

答案 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查询返回一组固定名称的固定列。