使用WHERE子句更新语句,该子句包含具有空值的列

luk*_*kik 9 sql postgresql sql-update

我正在使用另一个表中的数据更新一个表上的列.该WHERE子句基于多个列,其中一些列为null.根据我的想法,这个空值是throwing off你的标准UPDATE TABLE SET X=Y WHERE A=B陈述.

请参阅两个表的SQL Fiddle,其中我尝试table_one根据来自的数据进行更新table_two.我的查询目前看起来像这样:

UPDATE table_one SET table_one.x = table_two.y 
FROM table_two
WHERE 
table_one.invoice_number = table_two.invoice_number AND
table_one.submitted_by = table_two.submitted_by AND
table_one.passport_number = table_two.passport_number AND
table_one.driving_license_number = table_two.driving_license_number AND
table_one.national_id_number = table_two.national_id_number AND
table_one.tax_pin_identification_number = table_two.tax_pin_identification_number AND
table_one.vat_number = table_two.vat_number AND
table_one.ggcg_number = table_two.ggcg_number AND
table_one.national_association_number = table_two.national_association_number
Run Code Online (Sandbox Code Playgroud)

对于某些行的查询失败,table_one.x在任何一个表中的任何列都没有得到更新时null.即只有当所有列都有一些数据时才会更新.

这个问题与我早些时候在SO上的问题有关,那里我从大型数据集中获取了不同的值Distinct On.我现在想要的是使用具有唯一字段的表中的值填充大数据集.

UPDATE

我使用了@binotenary提供的第一个更新声明.对于小型表,它会在闪存中运行.示例有一个包含20,000条记录的表,更新在20秒内完成.但到目前为止,另一张有900万条记录的表已经运行了20个小时!请参阅下面的EXPLAIN功能输出

Update on table_one  (cost=0.00..210634237338.87 rows=13615011125 width=1996)
  ->  Nested Loop  (cost=0.00..210634237338.87 rows=13615011125 width=1996)
    Join Filter: ((((my_update_statement_here))))
    ->  Seq Scan on table_one  (cost=0.00..610872.62 rows=9661262 width=1986)
    ->  Seq Scan on table_two  (cost=0.00..6051.98 rows=299998 width=148)
Run Code Online (Sandbox Code Playgroud)

EXPLAIN ANALYZE选项还没完没了所以我取消它.

有关如何更快地进行此类更新的任何想法?即使这意味着使用不同的更新语句,甚至使用自定义函数循环并执行更新.

bin*_*ary 8

由于null = null评估false你需要检查两个字段是否null除了相等检查之外:

UPDATE table_one SET table_one.x = table_two.y 
FROM table_two
WHERE 
    (table_one.invoice_number = table_two.invoice_number 
        OR (table_one.invoice_number is null AND table_two.invoice_number is null))
    AND
    (table_one.submitted_by = table_two.submitted_by 
        OR (table_one.submitted_by is null AND table_two.submitted_by is null))
    AND 
    -- etc
Run Code Online (Sandbox Code Playgroud)

您还可以使用coalesce更具可读性的功能:

UPDATE table_one SET table_one.x = table_two.y 
FROM table_two
WHERE 
    coalesce(table_one.invoice_number, '') = coalesce(table_two.invoice_number, '')
    AND coalesce(table_one.submitted_by, '') = coalesce(table_two.submitted_by, '')
    AND -- etc
Run Code Online (Sandbox Code Playgroud)

但是你需要注意默认值(最后一个参数coalesce).
它的数据类型应该与列类型匹配(例如,您最终不会将日期与数字进行比较),默认值应该是它不会出现在数据中.
例如,coalesce(null, 1) = coalesce(1, 1)您希望避免这种情况.

更新(关于绩效):

Seq Scan on table_two- 这表明你没有任何索引table_two.
因此,如果您更新一行table_one然后在table_two数据库中找到匹配的行,则必须逐个扫描所有行,直到找到匹配为止.
如果相关列被索引,则可以更快地找到匹配的行.

另一方面,如果table_one有任何索引,则会降低更新速度.
根据这个性能指南:

表约束和索引会严重延迟每次写入.如果可能,您应该在更新运行时删除所有索引,触发器和外键,并在最后重新创建它们.

同一指南的另一个可能有用的建议是:

如果您可以使用(例如)顺序ID对数据进行分段,则可以批量逐步更新行.

因此,例如,如果您可以添加类似table_oneid

and table_one.id between x and y
Run Code Online (Sandbox Code Playgroud)

where条件和运行查询几次改变的值,x并且y使所有的行覆盖.

EXPLAIN ANALYZE选项也是永远的

在处理带有副作用的语句时,您可能需要小心使用该ANALYZE选项EXPLAIN.根据文件:

请记住,使用ANALYZE选项时实际执行该语句.尽管EXPLAIN将丢弃SELECT将返回的任何输出,但该语句的其他副作用将照常发生.