PostgreSQL:使用计数器更新 table 列的数据

PostgreSQL: Updating data on a column of a table with a counter

我想将我们数据库中 table 的列更改为 UNIQUE NOT NULL,首先我需要编写一个 postgres sql 脚本来修复当前数据。

我基本上需要做的是找到列 'code' 为 NULL 或 ' ' 的所有实例,并将其更改为如下文本:(1), (2), (3), ( 4)..

同样,我必须找到所有具有重复代码的实例并将它们更改为如下内容:'sameCode(1)'、'sameCode(2)'、'equalCode(1)'、'equalCode(2)'、'equalCode(3)'.

Example input       Expected Output
╔════╤═══════╗      ╔════╤══════════╗
║ id │ code  ║      ║ id │ code     ║
╠════╪═══════╣      ╠════╪══════════╣
║ 1  │ null  ║      ║ 1  │ (1)      ║
╟────┼───────╢      ╟────┼──────────╢
║ 2  │ ' '   ║      ║ 2  │ (2)      ║
╟────┼───────╢      ╟────┼──────────╢
║ 3  │ 'FOO' ║      ║ 3  │ 'FOO(1)' ║
╟────┼───────╢      ╟────┼──────────╢
║ 4  │ 'FOO' ║      ║ 4  │ 'FOO(2)' ║
╟────┼───────╢      ╟────┼──────────╢
║ 5  │ 'BAR' ║      ║ 5  │ 'BAR(1)' ║
╟────┼───────╢      ╟────┼──────────╢
║ 6  │ 'BOB' ║      ║ 6  │ 'BOB'    ║
╟────┼───────╢      ╟────┼──────────╢
║ 7  │ 'BAR' ║      ║ 7  │ 'BAR(2)' ║
╚════╧═══════╝      ╚════╧══════════╝

使用 SQL 编写带有计数器和更新的循环不是我的强项之一,我发现它有点困难。

从概念上讲,它很简单。获取按代码(以及 NULL 和 ' ')排序的 id 及其代码的有序列表,然后循环添加 '(++counter)' 到当前代码文本,但每次代码更改时重新启动计数器。这会很慢,但没关系,这只是一次性的事情,我们在这里讨论的不是数百万个实例。

DECLARE
    list_of_instances tablename[]; #dunno how to declare an instance
    counter INTEGER;
    oldcode CHARACTER VARYING;
BEGIN
    list_of_instances:=(select id, code from tablename 
                       where code is null or code = ' ' or code in 
                        (select code from tablename group by code having count(*) > 1) 
                       order by codi desc);
    counter:=1;
    oldcode:=tablename[0].code;
    for i in list_of_instances LOOP
        UPDATE tablename SET code=i.code + '(' + cast(counter as text) +')'
        WHERE id=i.id;
        counter:= counter+1;
        IF i.code <> oldcode THEN
            oldcode:=i.code;
            counter:=1;
        END IF;
    END LOOP;
END
$$ language 'plpgsql';

不用说这段代码不起作用。

您可以使用 window functions 生成您想要的号码。显然您想将 null 和仅包含空格的字符串视为同一事物,因此您需要将它们标准化。

以下查询生成您想要的数字,如果单个字符串出现多次,还会提供信息:

select id, code, 
       row_number() over (partition by coalesce(nullif(trim(code),''),'') order by id) as counter,
       count(*) over (partition by coalesce(nullif(trim(code),''),'')) as num_rows
from tablename
order by id;

根据您的样本输入,这个 returns:

id | code | counter | num_rows
---+------+---------+---------
 1 |      |       1 |        2
 2 |      |       2 |        2
 3 | FOO  |       1 |        2
 4 | FOO  |       2 |        2
 5 | BAR  |       1 |        2
 6 | BOB  |       1 |        1
 7 | BAR  |       2 |        2

表达式 coalesce(nullif(trim(code),''),'') "normalizes" NULL 仅由空格组成的值和字符串,例如' ' 到空字符串 ''.

现在可用于更新 table。由于您只想将计数器添加到多次出现的值,因此您需要将更新限制为:

update tablename as tn
  set code = concat(trim(tn.code), '(', x.counter, ')')
from (
  select id, code, 
         row_number() over (partition by coalesce(nullif(trim(code),''),'') order by id) as counter,
         count(*) over (partition by coalesce(nullif(trim(code),''),'')) as num_rows
  from tablename
) x
where x.id = tn.id
  and x.num_rows > 1;

更新后,您的示例如下所示:

id | code  
---+-------
 1 | (1)   
 2 | (2)   
 3 | FOO(1)
 4 | FOO(2)
 5 | BAR(1)
 6 | BOB   
 7 | BAR(2)

在线示例:http://rextester.com/WGAL85544