用连接把散开的数据接上
目标
根据场景选择内连接、左外连接、反连接和自连接,并解释 ON 与 WHERE 的区别。
为什么重要
连接时真正需要决定的是:是否保留没有匹配项的行。 若不明确,报表数字会悄悄出错。
左外连接后的 WHERE 尤其危险。对右表列设条件时,未匹配而填充 NULL 的行会因结果为未知而消失,外连接便悄悄变成内连接。另一个问题是扇出:一张订单有三条明细,连接结果就是三行,在其上汇总订单金额会得到三倍。必须始终意识到连接会改变行数。
步骤
- 连接订单与客户,把
order_id、customer_name、total_amount保存到视图v_order_customer。 - 创建视图
v_customer_orders,保存所有客户及其订单数。列为id、name、order_count;没有订单的客户为 0,总数仍为 400。 - 把从未下单客户的
id、name保存到视图v_never_ordered。 - 连接订单明细与商品,把
order_id、product_name、quantity、unit_price保存到视图v_item_detail。 - 针对
country为KR的客户中status为paid的订单,创建明细视图v_kr_paid_items。列必须以order_id、product_id开头。 - 创建视图
v_left_paid,保存所有客户及其已支付订单数。列为id、paid_count,客户仍为 400。 - 针对同城且
tier为vip的客户对,创建视图v_vip_pairs。列为a_id、b_id、city,同一对不能重复。 - 针对
status为paid、shipped、delivered之一、但没有完成付款的订单,创建视图v_unpaid_orders。
参考
- 先统计连接结果行数可以减少错误。
- 第 2 步确认
count(*)与count(컬럼)的区别。 - 常见错误 1:第 6 步把
AND o.status = 'paid'移到 WHERE 会减少客户数。 - 常见错误 2:第 8 步只检查付款行存在,会漏掉付款失败的订单。
为订单添加客户名称
连接订单与客户,把 order_id、customer_name、total_amount 保存到视图 v_order_customer。
在 ON 子句中写入连接两个表的列。遗漏连接条件会使行数按笛卡尔积增长。
保留没有订单的客户
创建视图 v_customer_orders,保存所有客户及其订单数。列为 id、name、order_count;没有订单的客户应为 0,客户总数仍须为 400。
使用保留左表所有行的连接。计数时若使用 count(*),没有匹配项的行也会被计为 1。
查找从未下单的客户
把从未下单客户的 id、name 保存到视图 v_never_ordered。
有多种方法检查不存在。NOT EXISTS 对 NULL 是安全的。
连接三个表
连接订单明细与商品,把 order_id、product_name、quantity、unit_price 保存到视图 v_item_detail。
可以连续使用多次连接。请检查每个连接是否都包含条件。
对连接结果应用条件
针对 country 为 KR 的客户中 status 为 paid 的订单,创建明细视图 v_kr_paid_items。列必须以 order_id、product_id 开头。
思考客户表条件与订单表条件各自应放在哪里。
确认 ON 与 WHERE 的区别
创建视图 v_left_paid,保存所有客户及其已支付订单数。列为 id、paid_count,客户总数仍须为 400。
在外连接中把右表条件移到 WHERE,会删除没有匹配项的行。请统计客户总数是否保持不变。
将表与自身连接
针对同城且 tier 为 vip 的客户对,创建视图 v_vip_pairs。列为 a_id、b_id、city,同一对不能重复出现。
给同一个表使用不同别名。要避免同一对出现两次,可在两个 id 之间使用不等关系条件。
查找未确认付款的订单
针对 status 为 paid、shipped、delivered 之一、但没有完成付款的订单,创建视图 v_unpaid_orders。
必须同时包含完全没有付款行和存在付款行但失败的情况。存在性条件内部也需要包含状态条件。