MYSQL 左联接不包括现有行

MYSQL left join include not existing rows

我目前已经创建了以下极其简化的 mysql table 来模仿我当前的 tables。

晋级 table:

CREATE TABLE promotion (
  promotion Varchar NOT NULL,
  PRIMARY KEY (promotion)
);
sale_id
10%
20%
30%

和销售 table:

CREATE TABLE sale (
  sale_id int NOT NULL AUTO_INCREMENT,
  promotion Varchar,
  date Date NOT NULL,
  amount int,
  PRIMARY KEY (sale_id)
);
sale_id promotion date amount
1 null 2022-1-1 100
2 10% 2022-1-1 100
3 10% 2022-1-1 100
4 20% 2022-1-1 100
5 null 2022-1-2 100
6 10% 2022-1-2 100
7 30% 2022-1-2 100
8 30% 2022-1-2 100

其中 promotion_id 是升级 table 的外键。所以关系是每次销售可以没有促销(null)或促销(促销主键的int id值作为外键)。

我正在尝试按月显示数据,然后按促销显示数据,其中每个月都有一堆按促销分组的销售金额总计。

到目前为止我有这个查询:

select
    Month(date),
    ifnull(promotion,'No Promo'),
    sum(amount)
from sale
    left join promotion p on p.promotion = sale.promotion
group by Month(date), sale.promotion

这给了我以下结果:

month promotion amount
1 null 100
1 10% 200
1 20% 100
2 null 100
2 10% 100
2 30% 200

但我想要的是:

month promotion amount
1 null 100
1 10% 200
1 20% 100
1 30% 0
2 null 100
2 10% 100
2 20% 0
2 30% 200

右加入不是意味着将促销列表中的每个促销都包含在促销 table 中吗?不管那个月是否有促销活动? 谁能给我一些正确方向的提示,说明为什么我的右连接不起作用?

您需要在联接中反转表的顺序。 (右连接也可以,但并非所有版本都支持)

select
    Month(date),
    ifnull(promotion,'No Promo'),
    sum(amount)
from promotion p
    left join sale on p.promotion = sale.promotion
group by Month(date), ifnull(promotion,'No Promo');

我稍微修改了你的查询和你的 table 定义,它在 dbFiddle 中工作正常。

CREATE TABLE promotion (
  promotion Varchar(4) NOT NULL
  PRIMARY KEY
);
INSERT INTO promotion VALUES ('10%'),('20%'),('30%');
CREATE TABLE sale (
  sale_id int NOT NULL PRIMARY KEY  AUTO_INCREMENT,
  promotion Varchar(4),
  date_ Date NOT NULL,
  amount int
);
insert into sale (promotion, date_, amount) values
(null,'2022-1-1',100),
('10%','2022-1-1',100),
('20%','2022-1-1',100),
('30%','2022-1-1',100),
(null,'2022-1-2',100),
('10%','2022-1-2',110),
('20%','2022-1-2',120),
('30%','2022-1-2',130),
(null,'2022-2-1',75),
('10%','2022-2-1',75),
('20%','2022-2-1',75),
('30%','2022-2-1',75),
(null,'2022-2-2',75),
('10%','2022-2-2',75),
('20%','2022-2-2',75),
('30%','2022-2-2',75);
select
    Month(date_) month_,
    ifnull(p.promotion,'No Promo') Promotion,
    sum(amount) Total
from sale
    left join promotion p on p.promotion = sale.promotion
group by Month(date_), ifnull(p.promotion,'No Promo');
month_ | Promotion | Total
-----: | :-------- | ----:
     1 | No Promo  |   200
     1 | 10%       |   210
     1 | 20%       |   220
     1 | 30%       |   230
     2 | No Promo  |   150
     2 | 10%       |   150
     2 | 20%       |   150
     2 | 30%       |   150

db<>fiddle here

我们需要将升级中的约束从主键更改为唯一以允许空值。然后,我们可以使用交叉连接创建所有行,然后左连接到 sale 以检索已售商品的价值。

CREATE TABLE promotion (
  promotion Varchar(4) 
  UNIQUE
);
INSERT INTO promotion VALUES (null),('10%'),('20%'),('30%');
CREATE TABLE sale (
  sale_id int NOT NULL PRIMARY KEY  AUTO_INCREMENT,
  promotion Varchar(4),
  date_ Date NOT NULL,
  amount int
);
insert into sale (promotion, date_, amount) values
(null,'2022-1-1',100),
('10%','2022-1-1',100),
('20%','2022-1-1',100),
('30%','2022-1-1',100),
(null,'2022-1-2',100),
('10%','2022-1-2',110),
('20%','2022-1-2',120),
('30%','2022-1-2',130),
(null,'2022-2-1',75),
('10%','2022-2-1',75),
('30%','2022-2-1',75),
(null,'2022-2-2',75),
('10%','2022-2-2',75),
('30%','2022-2-2',75);
select
    Month(date_) month_,
    ifnull(p.promotion,'No Promo') Promotion,
    sum(amount) Total
from sale
    left join promotion p on p.promotion = sale.promotion
group by Month(date_), ifnull(p.promotion,'No Promo');
month_ | Promotion | Total
-----: | :-------- | ----:
     1 | No Promo  |   200
     1 | 10%       |   210
     1 | 20%       |   220
     1 | 30%       |   230
     2 | No Promo  |   150
     2 | 10%       |   150
     2 | 30%       |   150
select 
  m,
  ifnull(p.promotion,'No Promo') Promotion,
  sum(amount) Total
from (select distinct month(date_) m from sale) m
cross join
promotion p
left join sale s on m.m = month(date_) and p.promotion = s.promotion
group by m, ifnull(p.promotion,'No Promo')
order by m, ifnull(p.promotion,'No Promo');;
 m | Promotion | Total
-: | :-------- | ----:
 1 | 10%       |   210
 1 | 20%       |   220
 1 | 30%       |   230
 1 | No Promo  |  null
 2 | 10%       |   150
 2 | 20%       |  null
 2 | 30%       |   150
 2 | No Promo  |  null

db<>fiddle here