在选择特定列从中提取的同时连接表

Joining tables while choosing which one a specific column pulls from

编辑:我使用的是 10.4.11-MariaDB 版本。这是一些代码来创建如图所示 tables

CREATE TABLE cats (
  c_id        INT            PRIMARY KEY   AUTO_INCREMENT,
  c_name      VARCHAR(255)   NOT NULL      UNIQUE
);

CREATE TABLE stock (
  p_id         INT            PRIMARY KEY   AUTO_INCREMENT,
  c_id        INT            NOT NULL,
  level         DECIMAL(10,2)  NOT NULL,
  rating   DECIMAL(10,2)  NOT NULL      DEFAULT 0.00
);

CREATE TABLE orders (
  p_id         INT            NOT NULL,
  sales           INT            NOT NULL
);

INSERT INTO cats (c_id, c_name) VALUES
(1, 'Boat'),
(2, 'Plane'),
(3, 'Car'), 
(4, 'Bike');

INSERT INTO stock (p_id, c_id, level, rating) VALUES
(1, 1, 145.65, 41),
(2, 1, 915.06, 49),
(3, 1, 981.36, 64),
(4, 1, 727.81, 29),
(5, 2, 678.19, 51),
(6, 2, 808.13, 43),
(7, 2, 711.10, 17),
(8, 3, 503.34, 92),
(9, 4, 292.41, 19),
(10, 4, 15.67, 36);


INSERT INTO orders (p_id, sales) VALUES
(1, 2),
(2, 4),
(9, 4),
(3, 2),
(8, 4),
(6, 3),
(2, 1),
(10, 2),
(8, 3),
(1, 4);

我将尝试彻底解决这个问题,因为我之前已经解决了这个问题,所以如果我需要提供任何其他信息,请告诉我。我将从代表我正在处理的数据的三个样本 table 开始,分别命名为 catsstockorders(全部小写,如果事项)。

基本思想是每个 p_id 都有一个关联的 ratinglevelsales 值,并且 p_id 值可以分类为他们共享 c_idstock中的每个p_id都是独一无二的,只属于一个c_id。我的目标是生成一个 table,它对按 c_name 分组的 ratinglevelsales 列的聚合执行一些简单的计算。我根据 c_id 所属的内容对每个 p_id 进行了颜色编码,希望能让事情更容易理解。下面是我想要生成的 table 的示例:

编辑:汽车的最低评分应为 92,而不是 8

如您所见,我想要select四列:

我还希望在底部有一行,其中包含对整个 p_id 值总体进行的相同计算,忽略类别。即,总和为# of P_id's、最小值为min rating、平均值为avg level、总和为# of sales的一行。请注意,orders不包括stock中的每个p_id,并且某些p_id值是重复的;重要的是中间三行每个 p_id 只包含一次,无论它们在 orders 中出现多少次。同样,最后一列必须对存在的 p_id 值的销售额求和并按类别对它们进行分组。

我写了这个查询来尝试生成这个:

SELECT c_name, COUNT(p_id), MIN(rating), FORMAT(AVG(level),2), (SELECT SUM(sales) FROM orders JOIN stock USING(p_id))

FROM stock

JOIN cats USING(c_id)

GROUP BY c_name ASC WITH ROLLUP

这几乎完全符合我的要求,除了最后一列有问题,它只显示所有 p_id 值组合的总销售额,而不考虑类别。我想在 SELECT 语句中使用子查询来计算此列,就好像我在 JOINing stock with cats 之后改为 LEFT JOIN orders with stock, 每个 列将仅根据在orders 中找到的p_id 值执行计算。我的想法是通过简单地 select 为我在子查询中需要的最后一列设置我需要的值来避免这种情况,但我正在努力弄清楚如何按 category_id 对结果进行分组从 product_idcategory_id 的映射仅存在于 stock.

本质上,我认为我的问题归结为不知道如何让我的中间三列基于 stock 中的 p_id 列聚合数据,同时确保最后一列只需要来自 orders。谁能给我建议?

如果我需要更具体的信息,请告诉我。

对于您的样本数据,这将起作用:

SELECT c.c_name, t.`# of p_ids`, t.`min rating`, t.`avg level`,
       COALESCE(
         SUM(o.sales),
         (SELECT SUM(sales) FROM orders)  
       )  `# of sales`
FROM (
  SELECT c_id, 
    COUNT(p_id) `# of p_ids`, 
    MIN(rating) `min rating`, 
    FORMAT(AVG(level), 2) `avg level`
  FROM stock
  GROUP BY c_id WITH ROLLUP
) t 
LEFT JOIN cats c ON c.c_id = t.c_id
LEFT JOIN stock s ON s.c_id = t.c_id
LEFT JOIN orders o ON o.p_id = s.p_id
GROUP BY t.c_id, c.c_name, t.`# of p_ids`, t.`min rating`, t.`avg level` 
ORDER BY c.c_name IS NULL, c.c_id

参见demo
结果:

| c_name | # of p_ids | min rating | avg level | # of sales |
| ------ | ---------- | ---------- | --------- | ---------- |
| Boat   | 4          | 29         | 692.47    | 13         |
| Plane  | 3          | 17         | 732.47    | 3          |
| Car    | 1          | 92         | 503.34    | 7          |
| Bike   | 2          | 19         | 154.04    | 6          |
|        | 10         | 17         | 577.87    | 29         |

您可以通过使用几个聚合作为 table 表达式来获得您想要的结果;然后你只需将它们连接在一起即可产生完整的结果。

例如:

select
  x.c_name,
  x.number_of_pids,
  x.min_rating,
  x.avg_level,
  s.sum_sales
from (
  select
    c.c_id,
    max(c.c_name) as c_name,
    count(distinct s.p_id) as number_of_pids,
    min(s.rating) as min_rating,
    avg(s.level) as avg_level
  from cats c
  left join stock s on s.c_id = c.c_id
  group by c.c_id
) x
left join (
  select
    s.c_id, sum(sales) as sum_sales
  from stock s
  left join orders o on o.p_id = s.p_id
  group by s.c_id
) s on s.c_id = x.c_id