--表tb1
CREATE TABLE tb1(
`id` UInt32,
`name` String,
`time` DateTime
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(time)
ORDER BY id;
--表 tb2
CREATE TABLE tb2(
`id` UInt32,
`rate` UInt8,
`time` DateTime
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(time)
ORDER BY id;
--表 tb3
CREATE TABLE tb3(
`id` UInt32,
`star` UInt8
) ENGINE = MergeTree()
ORDER BY id;
--插入数据
INSERT INTO tb1 VALUES
(1, 'ClickHouse', '2022-08-01 12:00:00')
(2, 'Spark', '2022-08-01 12:30:00')
(3, 'ElasticSearch', '2022-08-01 13:00:00')
(4, 'HBase', '2022-08-01 13:30:00')
(7, 'ClickHouse', '2022-08-01 14:00:00')
(8, 'Spark', '2022-08-01 14:30:00');
INSERT INTO tb2 VALUES
(1, 100, '2022-08-01 11:55:00')
(1, 105, '2022-08-01 11:50:00')
(2, 90, '2022-08-01 12:01:00')
(3, 80, '2022-08-01 13:10:00')
(5, 70, '2022-08-01 14:00:00')
(6, 60, '2022-08-01 13:55:00');
INSERT INTO tb3 VALUES
(1, 1000)
(2, 900);
官方文档详见join clause
SELECT <expr_list>
FROM <left_table>
[GLOBAL] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI|ANY|ASOF] JOIN <right_table>
(ON <expr_list>)|(USING <column_list>) ...
join_default_strictness,默认为ALL,可能的行为包括如下:
ALL —如果右表有几个匹配的行,ClickHouse就会从匹配的行创建一个笛卡尔积。这是标准SQL中的正常JOIN行为。ANY —如果右表有几个匹配的行,则只连接第一个找到的行。如果右表只有一行匹配,则ANY和ALL的结果是相同的。ASOF —用于连接不确定匹配的序列。Empty string —如果查询中没有指定ALL或ANY, ClickHouse将抛出异常。select a.id,a.name,b.rate from tb1 a inner join tb2 b using id;
select a.id,a.name,b.rate from tb1 a all inner join tb2 b using id;

如果左表内的一行数据,在右表中有多行数据与之连接匹配,则仅返回右表中第一行连接的数据。连接依据同样为:left.key=right.key

select a.id,a.name,b.rate,a.time,b.time from tb1 a asof join tb2 b on a.id=b.id and a.time>=b.time
select a.id,a.name,b.rate,a.time,b.time from tb1 a asof join tb2 b using(id,time)

select a.id,a.name,b.rate,a.time,b.time from tb1 a left asof join tb2 b on a.id=b.id and a.time>=b.time
select a.id,a.name,b.rate,a.time,b.time from tb1 a left asof join tb2 b using(id,time)

select a.id,a.name from tb1 a semi join tb2 b on(a.id=b.id);
select a.id,a.name from tb1 a semi join tb2 b using id;
select a.id,a.name from tb1 a semi join tb2 b on(a.id=b.id);

select a.id,a.name from tb1 a anti join tb2 b using id;
select a.id,a.name from tb1 a anti join tb2 b on(a.id=b.id);

ClicHouse分布式join通常涉及到左右表为分布式表,分布式执行过程中需要将数据在节点间进行交换,
将数据在节点间交换的动作在分布式执行计划中称为数据的流动streaming算子,
ClickHouse支持的streaming算子有如下三种:
其实对于ClickHouse来说,说是实现了Shuffle JOIN还比较勉强,其只实现了类Broadcast JOIN类型,ClickHouse当前的分布式join查询框架更多的还是实现了两阶段查询任务
与ClickHouse相比通常业界MPP数据库分布式join查询框架模型的数据在节点间交换Streaming算子通常为以下几种:

Broadcast join对于大小表关联,需要将小表数据放在右边;
1)有如下分布式Join SQL语句:

2)执行过程如下:

3)总结:
Colocate join需要将join key字段使用相同的分布算法,将分布键相同数据分布在同一个计算节点。
1) 有如下分布式Join SQL语句:

2) 执行过程如下:

3) 总结:
由于数据已经进行了预分区/分布,相同的JOIN KEY对应的数据一定存储在同一个计算节点,join计算过程中不会进行跨节点的数据交换工作,所以无需对右表做分布式查询,也能获得正确结果,并且性能较优。
4) Colocate JOIN原理:
根据“相同JOIN KEY必定相同分片”原理,
将涉及JOIN计算的表,按JOIN KEY在集群维度作分片。将分布式JOIN转为节点的本地JOIN,极大减少了查询放大问题。按如下操作:
5) Colocate Join场景约束
生成整个表的笛卡尔积,不需要指定“连接键”。【 produces cartesian product of whole tables, “join keys” are not specified.】
select a.id,a.name,b.rate,b.time from tb1 a cross join tb2 b;