Redshift中UPDATE语句用JOIN与LEFT JOIN的差异及报错解析
我明白你已经对JOIN和LEFT JOIN的基础差异烂熟于心,所以咱们直接聚焦Redshift在UPDATE场景下的特殊执行逻辑,拆解你遇到的两个语句的核心差异。
首先看你报错的语句:
UPDATE billing_temp SET spotlink = sl.spotlink FROM billing_temp AS bt LEFT JOIN spot_link AS sl ON bt.dupeid = sl.dupeid;
它抛出的错误是:Error: Target table must be part of an equijoin predicate
再对比正常运行的语句:
UPDATE public.billingcombined SET revenue_type = r.revenue_type FROM public.billingcombined AS b JOIN public.revenuetype AS r ON b.contract_number = r.contract_number;
先搞懂Redshift要求的「equijoin predicate」是什么
在Redshift的UPDATE逻辑里,「equijoin predicate」指的是目标表必须和FROM子句中的某个表(包括自身的别名)建立明确的等值连接关系,目的是让Redshift能精准定位:目标表的每一行,应该对应FROM子句结果集中的哪一行来获取更新值,避免出现「一行被多次更新」或者「无法匹配到明确更新源」的歧义。
为什么LEFT JOIN的语句会报错?
你的第一个语句犯了一个关键的隐含错误:目标表billing_temp和FROM子句里的自身别名bt之间,没有任何显式的等值连接条件。
虽然你用bt和spot_link做了LEFT JOIN,但Redshift根本不知道如何把billing_temp(要更新的表)和bt(FROM里的表)关联起来。哪怕它们是同一个表,Redshift也不会默认做全列等值关联——它需要你明确给出一个等值条件(比如billing_temp.dupeid = bt.dupeid),来满足「equijoin predicate」的要求。
另外,LEFT JOIN本身的特性会保留左表(bt)的所有行,包括那些在spot_link中没有匹配的行(此时sl.spotlink为NULL)。如果没有明确的等值连接,Redshift无法判断这些NULL值应该对应目标表的哪一行,自然会抛出错误。
为什么INNER JOIN的语句能正常运行?
你的第二个语句里,虽然没有显式写目标表和b的连接条件,但Redshift在这里做了一个「隐式处理」:当FROM子句中引用了目标表的别名(b是public.billingcombined的别名),且使用的是INNER JOIN时,Redshift会默认通过目标表的主键或唯一约束列,将目标表与别名表做等值关联。
同时,INNER JOIN的结果集中,每一行b都能对应到目标表的唯一一行(假设contract_number是唯一的),不会产生歧义,所以Redshift可以安全地执行更新。
如何修改LEFT JOIN的语句使其正常运行?
只需要给目标表和FROM子句里的自身别名加上明确的等值连接条件即可,比如:
UPDATE billing_temp SET spotlink = sl.spotlink FROM billing_temp AS bt LEFT JOIN spot_link AS sl ON bt.dupeid = sl.dupeid WHERE billing_temp.dupeid = bt.dupeid;
这样就满足了Redshift对「equijoin predicate」的要求,它能明确知道目标表的每一行对应bt的哪一行,哪怕是LEFT JOIN产生的NULL值,也能正确更新到对应的目标行。
内容的提问来源于stack exchange,提问作者Mr. Spock

