必须在on子句中显式写出复合主键全部字段并用and连接,缺一不可;字段名、类型、语义须一一对应,类型不一致会导致隐式转换丢索引或报错,null值参与等值判断会使条件返回unknown,造成“假匹配”,需提前过滤或用coalesce等安全处理。

必须在 ON 子句里显式写出所有主键字段,缺一不可;漏写一个,结果就不是“连不上”,而是“连错了”。
复合主键 JOIN 必须用 AND 连接全部等值条件
比如 orders 表主键是 (customer_id, order_date),你要和 customers 表关联,不能只写 o.customer_id = c.id。那样会导致同一个客户的所有订单都映射到同一行客户记录上,丢失时间维度区分。
正确写法是:
SELECT o.*, c.name FROM orders o JOIN customers c ON o.customer_id = c.id AND o.order_date = c.registration_date;
- 每个主键字段都得单独写一次等值判断,用
AND连接 - 字段顺序无关紧要,但字段名、类型、语义必须一一对应
- 别用
USING:它只支持同名列,而复合主键字段名往往不统一(如cust_idvscustomer_id)
字段类型不一致会 silently 失效或报错
常见陷阱是 orders.customer_id 是 INT,而 customers.id 是 VARCHAR(32)。MySQL 可能隐式转换但丢弃索引;PostgreSQL 直接报错 operator does not exist。
实操建议:
一款AI工具,主要用于在主代理响应前,并行运行Kimi K2.5和GPT 5.3 Codex,注入双方观点以增强认知多样性,适合需要提升相关任务效率的用户。
- 先查清类型:
DESCRIBE orders;或\d customers(psql) - 类型不同时加显式转换:
CAST(o.customer_id AS TEXT)(PostgreSQL)、CONVERT(o.customer_id, CHAR)(MySQL) - 长期方案:建表时对齐类型,比如都用
VARCHAR(32)存 UUID 类主键
LEFT JOIN 遇到 NULL 主键字段会“假匹配”
如果 orders 表某行的 order_date 是 NULL,那么整个 ON o.customer_id = c.id AND o.order_date = c.registration_date 表达式返回 UNKNOWN,该行仍保留在结果中,右表字段全为 NULL——看起来像“没连上”,其实是 NULL 参与了等值判断。
应对方式:
- 提前检查:
SELECT COUNT(*) FROM orders WHERE customer_id IS NULL OR order_date IS NULL; - 业务允许时过滤掉:
WHERE o.customer_id IS NOT NULL AND o.order_date IS NOT NULL - 需保留 NULL 行时,改用
COALESCE(o.order_date, '1970-01-01') = COALESCE(c.registration_date, '1970-01-01')
复合 JOIN 条件必须配复合索引,单列索引没用
优化器不会合并两个单列索引去加速 ON a.x = b.x AND a.y = b.y。哪怕你给 x 和 y 都建了单列索引,EXPLAIN 里 key_len 通常只显示一个字段长度,rows 却暴增。
必须为每张表建复合索引,且字段顺序要和 ON 中出现顺序一致(至少前缀匹配):
- 若
ON o.user_id = u.id AND o.tenant_id = u.tenant_id,则orders表建INDEX idx_orders_user_tenant (user_id, tenant_id) - 字段顺序按选择性排:高区分度、高频等值字段放最左;低区分度字段(如
status)别放最左 - 用
SHOW INDEX FROM table_name查看Cardinality,确认是否真有区分度
真正容易被忽略的,是类型对齐和 NULL 处理——它们不会让 SQL 报错,却会让结果集悄然失真,且难以复现。










