PostgreSQL - 使用正则表达式拆分行

时间:2016-02-20 19:31:30

标签: regex database postgresql

我有一张表,其中包含colums ID,名称和描述。表格如下:

ID    |  Name | Model
1     |  Ford | Focus 3
2.1-3 |  Opel | 1. Astra 2
      |       | 2. Vectra 2
      |       | 3. Vectra 3
3.1-2 | Toyota| Avensis 2; Micra 
4.1-2 |  Opel | (various versions) 1. Astra
      |       | (various versions) 2. Vectra
5.1-3 | Mazda | MX5; GTR; MX4

我想使用" regexp_split_to_table"将其拆分为新行。 得到这样的结果:

ID    |  Name | Description
1     |  Ford | Focus 3
2.1   |  Opel | Astra 2
2.2   |  Opel | Vectra 2
2.3   |  Opel | Vectra 3
3.1   | Toyota| Avensis
3.2   | Toyota| Micra
4.1   |  Opel | Astra
4.2   |  Opel | Vectra
5.1   | Mazda | MX5
5.2   | Mazda | GTR
5.3   | Mazda | MX4

如何使用postresql并在此之后更新主表?

非常感谢你的帮助!

1 个答案:

答案 0 :(得分:3)

一般来说,由于以下几个原因,这是不可能的:

您的数据未订购,例如,可能有类似

的数据
ID    |  Name | Description
      |       | (year)  2. 2004
1     |  Ford | Some text
5.1-3 | Mazda | Petrol; 1.9; 3-doors
      |       | 2. Diesel
2.1-3 |  Opel | 1. Astra
      |       | 3. 2005
3     | Toyota| 2001; Petrol; 1.8 TDI
4.1-2 |  Opel | (model) 1. Vectra

尝试类似

的内容
drop table if exists my_ugly_table;
create table my_ugly_table as select generate_series(1,3) as x, generate_series(1,3) as y;
select * from my_ugly_table;
update my_ugly_table set y = 4 where x = 2;
select * from my_ugly_table;

你会有

第一个结果:

 x | y 
---+---
 1 | 1
 2 | 2
 3 | 3
(3 rows)

第二个结果:

 x | y 
---+---
 1 | 1
 3 | 3
 2 | 4
(3 rows)

如您所见,行顺序已更改。

接下来,您想要在ID中保留一些有价值的数据,例如1.2, 1.3等等,您的目标是错误的。 ID必须只是行的唯一标识符,而不是其他内容。理想情况下,您对ID的值没有任何了解 - 它们只是存在。

但是,您可以尝试使用plpgsql或类似的内容对原始数据执行某些操作:

首先创建表格,我们将使用我们的数据执行不良操作:

create table models_t as select * from models m where 1=2;

它将创建一个空表models_t,其结构与表models相同。

最后为他们创造真正的PK:

alter table models_t add mt_id serial not null primary key; 

接下来让我们按数据填写:

do language plpgsql $$
declare
  p_rec models;
  c_rec models;
begin
  p_rec := null;
  for c_rec in (select * from models) loop
    p_rec.id := coalesce(c_rec.id, p_rec.id);
    p_rec.name := coalesce(c_rec.name, p_rec.name);
    p_rec.description := c_rec.description;
    insert into models_t values (p_rec.id, p_rec.name, p_rec.description);
    raise notice '% %', c_rec.id, c_rec.name;
  end loop;
end; $$;

只有一个结果是我们有没有间隙的表,如:

postgres=# select * from models_t;
  id   |  name  |     description      | mt_id 
-------+--------+----------------------+-------
 1     | Ford   | Some text            |     1
 2.1-3 | Opel   | 1. Astra             |     2
 2.1-3 | Opel   | 2. Diesel            |     3
 2.1-3 | Opel   | 3. 2005              |     4
 3     | Toyota | 2001; Ptrol; 1.8 TDI |     5
 4.1-2 | Opel   | (model) 1. Vectra    |     6
 4.1-2 | Opel   | (year) 2. 2004       |     7
 5.1-3 | Mazda  | Petrol; 1.9; 3-doors |     8
(8 rows)

实际上这已经足够了。但是,让我们解析我们的最后数据:

select 
  *, 
  substring(id from '(\d*)\.?.*') as main_id, -- First number before dot
  row_number() over (partition by substring(id from '(\d*)\.?.*')) as secondary_id -- Order inside previous value
from models_t;

结果:

  id   |  name  |     description      | mt_id | main_id | secondary_id 
-------+--------+----------------------+-------+---------+--------------
 1     | Ford   | Some text            |     1 | 1       |            1
 2.1-3 | Opel   | 1. Astra             |     2 | 2       |            1
 2.1-3 | Opel   | 2. Diesel            |     3 | 2       |            2
 2.1-3 | Opel   | 3. 2005              |     4 | 2       |            3
 3     | Toyota | 2001; Ptrol; 1.8 TDI |     5 | 3       |            1
 4.1-2 | Opel   | (model) 1. Vectra    |     6 | 4       |            1
 4.1-2 | Opel   | (year) 2. 2004       |     7 | 4       |            2
 5.1-3 | Mazda  | Petrol; 1.9; 3-doors |     8 | 5       |            1
(8 rows)

此时,我们可以使用列main_idsecondary_id来构建想要的ID,例如1.12.3

其他一切都取决于你。

祝你好运,玩得开心。