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)
我想将我们数据库中 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)