Postgresql-根据最大行数更新无效单元格

时间:2012-02-14 02:37:33

标签: sql postgresql group-by subquery max

POSTGRES- 我想在Employees表中更新Employees.zipcode_mod列中的'invalid zipcodes'(Employees.zipcode),如果它们在Ref_Zips.zip5中不存在则无效

更新规则是查找3个字符或长的所有无效zipcode,并在Tmp_Agg_Zips.zip列的前三位数字上匹配它们,并使用Tmp_Agg_Zips.emp_cnt最高的Tmp_Agg_Zips.zip更新Employees.zipcode_mod。如果多个Tmp_Agg_Zips.zip值之间存在平局,则获取“最高”的zip值。

更新

如果无效zipcode超过3个字符,但其前三个数字与Tmp_Agg_Zips.zip的前三个数字中的任何一个都不匹配,或者无效zipcode小于3个字符或无效,然后只需使用Employees.zipcode_mod更新Tmp_Agg_Zips.zip,其最大值为Tmp_Agg_Zips.emp_cnt,无论前三位数如何。在下面的示例中,Ex-88888和null更新为10012。

这适用于 Postgres 8.4

员工

Gender | zipcode | zipcode_mod
   M   |  99574  |
   F   |  99574  |
   F   |  10012  |
   F   |  10012  |
   F   |  10012  |
   F   |  19001  |
   M   |    100  | 10012
   M   |    190  | 19001
   M   |     19  | 10012
   F   |   null  | 10012
   F   |  88888  | 10012
   F   |   8888  | 10012

Tmp_Agg_Zips

  zip | emp_cnt
99574 |  2
10012 |  3
19001 |  1

Ref_Zips

zip5
99574
10012
19001

1 个答案:

答案 0 :(得分:1)

更新问题

我添加了一个COALESCE()子句来捕获没有找到匹配替代的情况。并将默认值的计算放入子查询中以供多次使用。

UPDATE employees e
SET    zipcode_mod =
   CASE WHEN length(e.zipcode) > 2 THEN
      COALESCE((
         SELECT t.zip
         FROM   tmp_agg_zips t
         WHERE  substr(t.zipcode, 1, 3) = substr(e.zipcode, 1, 3)
         ORDER  BY t.emp_cnt DESC, t.zip  -- lowest zip for mult. emp_cnt
         LIMIT  1
         ), t0.zip)
   ELSE
      t0.zip
   END
FROM  (
   SELECT zip
   FROM   tmp_agg_zips
   ORDER  BY emp_cnt DESC, t.zip
   LIMIT  1
   ) t0
WHERE  NOT EXISTS (
   SELECT *
   FROM   ref_zips r
   WHERE  r.zip5 = e.zipcode
   )

原始问题

此查询适用于PostgreSQL的旧版本

UPDATE employees e
SET    zipcode_mod =
    CASE WHEN length(e.zipcode) > 2 THEN (
        SELECT t.zip
        FROM   tmp_agg_zips t
        WHERE  substr(t.zipcode, 1, 3) = substr(e.zipcode, 1, 3)
        ORDER  BY t.emp_cnt DESC, t.zip -- lowest zip for mult. emp_cnt
        LIMIT  1
        )
    ELSE (
        SELECT zip
        FROM   tmp_agg_zips
        ORDER  BY emp_cnt DESC, t.zip
        LIMIT  1
        )
    END
WHERE  NOT EXISTS (
        SELECT *
        FROM   ref_zips r
        WHERE  r.zip5 = e.zipcode
        )

PostgreSQL 9.1 中,CTE应该效果更佳:

WITH x AS (
    SELECT zip
    FROM   tmp_agg_zips
    ORDER  BY emp_cnt DESC, t.zip
    LIMIT  1
    )
UPDATE employees e
SET    zipcode_mod =
    CASE WHEN length(e.zipcode) > 2 THEN (
        SELECT t.zip
        FROM   tmp_agg_zips t
        WHERE  left(t.zipcode, 3) = left(e.zipcode, 3)
        ORDER  BY t.emp_cnt DESC, t.zip  -- pick lowest zip
        LIMIT  1
        )
    ELSE
        x.zip
    END
FROM   x
WHERE  NOT EXISTS (
        SELECT *
        FROM   ref_zips r
        WHERE  r.zip5 = e.zipcode
        )

如果tmp_agg_zips中有多个行具有相同(最高)emp_cnt,我选择“最低”zip。你没有具体说明如何打破这些联系。

BTW,邮政编码的不同列名对我没有帮助。表格限定列名称可以做得更好。