I have a column with multiple comma-separated values (up to 50, country names). Those values must be replaced with a particular code (country code, ISO3) from another table.

Internet search has lead me to split the values to an array (regex_split_to_array) and to use a loop through the array (FOREACH LOOP)

I get the general concept but I am struggling at least with syntax (pqsl beginner).

Approach:

DO
$do$
DECLARE
m   varchar[];
arr varchar[]; -- declare array
BEGIN
-- split the comma-separeted values to array and insert into the array
SELECT (regexp_split_to_array(country, E','))
  into arr
FROM tablename
WHERE xy;
FOREACH m SLICE 1 IN ARRAY arr
LOOP
-- update/replace the value with a function
RAISE NOTICE 'another function(%)', m;
END LOOP;

END
$do$

I assume that filling the array doesn't work that way..

PostgreSQL 9.1

share|improve this question
    
And why are you using an un-supported and outdated Postgres version? – a_horse_with_no_name 20 hours ago
    
I am not the server-admin, I know about that and already reported it. Thanks. – Hannes Bl 18 hours ago
up vote 1 down vote accepted

You don't need a loop or PL/pgSQL for this. You can do this with a single statement:

Assuming the following setup:

create table  tablename
(
  id integer primary key,
  country_list text
);

create table lookup
(
  iso_code varchar(3),
  country text
);

insert into tablename
values
(1, 'Germany, Austria, Italy'),
(2, 'Austria, France'),
(3, 'France, Switzerland');

insert into lookup
values
('de', 'Germany'),
('at', 'Austria'),
('it', 'Italy'),
('fr', 'France'),
('ch', 'Switzerland');

You can unnest the countries using:

select s.id, trim(unnest(string_to_array(s.country_list, ','))) as country
from tablename s;

Given the sample data above that returns the following:

id | country    
---+------------
 1 | Germany    
 1 | Austria    
 1 | Italy      
 2 | Austria    
 2 | France     
 3 | France     
 3 | Switzerland

This can be joined to your lookup table:

with normalized as (
   select s.id, trim(unnest(string_to_array(s.country_list, ','))) as country
   from tablename s
)
select n.id, n.country, l.iso_code
from normalized n
  join lookup l on l.country = n.country;

This returns the following:

id | country     | iso_code
---+-------------+---------
 1 | Austria     | at      
 1 | Germany     | de      
 1 | Italy       | it      
 2 | Austria     | at      
 2 | France      | fr      
 3 | France      | fr      
 3 | Switzerland | ch      

You can aggregate the list of ISO codes back into your de-normalized structure:

with normalized as (
   select s.id, trim(unnest(string_to_array(s.country_list, ','))) as country
   from tablename s
)
select n.id, string_agg(l.iso_code,',') as iso_list
from normalized n
  join lookup l on l.country = n.country
group by n.id;

And that can be used to replace the values in the target table:

with normalized as (
   select s.id, trim(unnest(string_to_array(s.country_list, ','))) as country
   from tablename s
), translated as (
  select n.id, string_agg(l.iso_code,',') as iso_list
  from normalized n
    join lookup l on l.country = n.country
  group by n.id
)
update tablename st
  set country_list = t.iso_list
from translated t
where t.id = st.id;

After that the contents of tablename is:

id | country_list
---+-------------
 1 | it,at,de    
 2 | fr,at       
 3 | fr,ch       

A much better solution would be to properly normalize your model and create a many-to-many mapping table between tablename and the lookup_table

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.