Postgres - 替换相应唯一标识符的列中的各种名称

时间:2013-09-27 17:07:43

标签: sql postgresql

以下列表是指结婚前后的人名。随着时间的推移,他们中的一些人离婚,再次结婚和/或改名 我想要做的是获取该人一生中所有的名字,并为每个人添加一个带有唯一标识符的新列。

这是名为Names的实际列表:

Name_before                  Name_after         
Misti Gulick                 Misti Gulick Thibodeaux            
Faye Leaton                  Faye Leaton Hemby          
Arden Peck                   Arden Peck Mroz            
Carlton Kingsley             Carlton Kingsley Mcelveen          
Dolly Verhey                 Dolly Verhey Irish             
Gaynell Pasquale             Gaynell Pasquale Ayala             
Misti Gulick Thibodeaux      Misti Thibodeaux           
Faye Leaton Hemby            Faye Hemby         
Arden Peck Mroz              Arden Mroz         
Carlton Kingsley Mcelveen    Carlton Mcelveen           
Dolly Verhey Irish           Dolly Irish            
Gaynell Pasquale Ayala       Gaynell Ayala          
Misti Thibodeaux             Misti Trey Thibodeaux          
Faye Hemby                   Faye Barrett Hemby         
Arden Mroz                   Arden Justin Mroz          
Carlton Mcelveen             Carlton Tameka Mcelveen            
Dolly Irish                  Dolly Jeremiah Irish           
Gaynell Ayala                Gaynell Cherry Ayala           

理想的列表如下:

Name_before                 Name_after                  Identifier
Misti Gulick                Misti Gulick Thibodeaux     Misti Gulick 
Faye Leaton                 Faye Leaton Hemby           Faye Leaton 
Arden Peck                  Arden Peck Mroz             Arden Peck 
Carlton Kingsley            Carlton Kingsley Mcelveen   Carlton Kingsley 
Dolly Verhey                Dolly Verhey Irish          Dolly Verhey 
Gaynell Pasquale            Gaynell Pasquale Ayala      Gaynell Pasquale 
Misti Gulick Thibodeaux     Misti Thibodeaux            Misti Gulick 
Faye Leaton Hemby           Faye Hemby                  Faye Leaton 
Arden Peck Mroz             Arden Mroz                  Arden Peck 
Carlton Kingsley Mcelveen   Carlton Mcelveen            Carlton Kingsley 
Dolly Verhey Irish          Dolly Irish                 Dolly Verhey 
Gaynell Pasquale Ayala      Gaynell Ayala               Gaynell Pasquale 
Misti Thibodeaux            Misti Trey Thibodeaux       Misti Gulick 
Faye Hemby                  Faye Barrett Hemby          Faye Leaton 
Arden Mroz                  Arden Justin Mroz           Arden Peck 
Carlton Mcelveen            Carlton Tameka Mcelveen     Carlton Kingsley 
Dolly Irish                 Dolly Jeremiah Irish        Dolly Verhey 
Gaynell Ayala               Gaynell Cherry Ayala        Gaynell Pasquale 

我尝试做的是在Name_before中遇到Name_after中的常用值,并重复执行,直到我没有更多匹配为止。
每次创建其中一个表时,名称数量将减少。

create table name_temp1 as 
   select * 
      from Names 
          where Name_after in (select distinct(Name_before) from Names)
           order by Name_before, Name_after;                    

create table name_temp2 as 
  select * 
     from name_temp1 
       where Name_after in (select distinct(Name_before) from name_temp1) 
           order by Name_before, Name_after;            


create table name_temp3 as 
   select * 
      from name_temp2 
         where Name_after in (select distinct(Name_before) from name_temp2) 
           order by Name_before, Name_after;

然后我会使用带有“case”功能的查询:

select *,case when n3.Name_before=n2.Name_after 
    then case when n2.Name_before=n1.Name_after 
       then n1.Name_after else n.after end end end 
            from Names n,  name_temp1 n1, name_temp2 n2, name_temp3 n3;

我知道这根本不优雅,没有表现。你们中的一些人会帮我改进吗?或者甚至欢迎其他建议!!谢谢,

2 个答案:

答案 0 :(得分:1)

模式

整个过程的目标应该是规范化架构:表格person包含代理主键 person_id(因为那里)没有明显的自然主键)。我建议您使用serial列。
还有一个表person_name,其中包含person的外键:

CREATE TEMP TABLE person(
   person_id serial PRIMARY KEY  -- implicit primary key constraint
   -- probably more attributes belonging to the person
 );

CREATE TEMP TABLE person_name(
   person_name_id  serial PRIMARY KEY
  ,person_id       int NOT NULL REFERENCES person(person_id) -- foreign key
  ,name            text NOT NULL
  ,step            int DEFAULT 0
   -- possibly more attributes that belong to the person at this step only
 );

(person_id, name)无法成为UNIQUE,因为同一个人在一生中可以多次使用相同的名称。

为了提炼数据,我想您使用recursive CTE的单个查询。但是,如果任何人共享相同名称,则您的操作必然是不明确的。如果没有其他信息,您可能会有无意义的结果或循环依赖关系。

person_name step = 0中的行将保留"Identifier"

查询

为了这个查询,我假设UNIQUE名称(或者它不能正常工作。)。

WITH RECURSIVE p_start AS (
   SELECT row_number() OVER (ORDER BY n.name_before) AS person_id, n.*
   FROM   names n
   LEFT   JOIN names n2 ON n2.name_after = n.name_before
   WHERE  n2.name_after IS NULL
   )
, pers AS (
   SELECT person_id, name_after AS name, 1 AS step
   FROM   p_start

   UNION  ALL
   SELECT p.person_id, n.name_after, p.step + 1
   FROM   pers p
   JOIN   names  n ON n.name_before = p.name
   -- WHERE  p.step < 10 -- If query doesn't finish, stop the infinite recursion
   )
SELECT person_id, name_before AS name, 0 AS step
FROM   p_start
UNION ALL
SELECT person_id, name, step
FROM   pers
ORDER  BY person_id, step

-> SQLfiddle demo.

一站式店

使用上述架构,您可以使用单个查询执行所有内容:填写新表并返回结果:

WITH RECURSIVE p_start AS (
   SELECT row_number() OVER (ORDER BY n.name_before) AS person_id, n.*
   FROM   names n
   LEFT   JOIN names n2 ON n2.name_after = n.name_before
   WHERE  n2.name_after IS NULL
   )
, pers AS (
   SELECT person_id, name_after AS name, 1 AS step
   FROM   p_start

   UNION  ALL
   SELECT p.person_id, n.name_after, p.step + 1
   FROM   pers p
   JOIN   names  n ON n.name_before = p.name
   -- WHERE  p.step < 10 -- If query doesn't finish, stop the infinite recursion
   )
, ins_person AS (
   INSERT INTO person(person_id)
   SELECT person_id FROM p_start
   )
INSERT INTO person_name(person_id, name, step)
SELECT person_id, name_before, 0 AS step
FROM   p_start
UNION ALL
SELECT person_id, name, step
FROM   pers
ORDER  BY person_id, step
RETURNING *

-> SQLfiddle demo.

最后,person的初始化序列,以便您以后不会出现重复的密钥违规行为:

SELECT setval('person_person_id_seq', (SELECT max(person_id) FROM person))

答案 1 :(得分:1)

DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;

  -- make some data
CREATE TABLE names_org
        ( name_id SERIAL NOT NULL PRIMARY KEY
        , name_org varchar
        , name_new varchar
        );
COPY names_org (name_org,name_new) FROM stdin;
Misti Gulick    Misti Gulick Thibodeaux
Faye Leaton     Faye Leaton Hemby
Arden Peck      Arden Peck Mroz
Carlton Kingsley        Carlton Kingsley Mcelveen
Dolly Verhey    Dolly Verhey Irish
Gaynell Pasquale        Gaynell Pasquale Ayala
Misti Gulick Thibodeaux Misti Thibodeaux
Faye Leaton Hemby       Faye Hemby
Arden Peck Mroz Arden Mroz
Carlton Kingsley Mcelveen       Carlton Mcelveen
Dolly Verhey Irish      Dolly Irish
Gaynell Pasquale Ayala  Gaynell Ayala
Misti Thibodeaux        Misti Trey Thibodeaux
Faye Hemby      Faye Barrett Hemby
Arden Mroz      Arden Justin Mroz
Carlton Mcelveen        Carlton Tameka Mcelveen
Dolly Irish     Dolly Jeremiah Irish
Gaynell Ayala   Gaynell Cherry Ayala
\.

SELECT * FROM names_org;

更改和更新(为清晰起见,分步骤)

  --Add a few self-referencing fields
  --
ALTER TABLE names_org
        -- points to the **first** entry for this person
        ADD COLUMN canon_id INTEGER
                REFERENCES names_org (name_id)
        -- points to the **nearest previous** entry for this person
        , ADD COLUMN parent_id INTEGER
                REFERENCES names_org (name_id)
        ;

        -- Update from **the nearest** previous record; if any
UPDATE names_org dst
SET parent_id = src.name_id
FROM names_org src
   -- src is the previous row for this person
WHERE src.name_new = dst.name_org
AND src.name_id < dst.name_id
   -- The nearest: eliminate the middlemen
AND NOT EXISTS (SELECT *
        FROM names_org nx
        WHERE nx.name_new = dst.name_org
        AND nx.name_id < dst.name_id
        AND nx.name_id > src.name_id
        );

   -- Add the final newnames (at the end of the chains) to the table, too.
   -- These are the name strings that only occur in name_new,
   -- but never in name_org
INSERT INTO names_org (name_org, parent_id)
SELECT name_new, name_id
FROM names_org src
WHERE NOT EXISTS (
        SELECT *
        FROM names_org nx
        WHERE nx.parent_id = src.name_id
        );

        -- Find canonical parent (the head of the chain)
WITH RECURSIVE list AS (
        SELECT name_id AS canon_id
        , name_id AS this_id
        FROM names_org
        WHERE parent_id IS NULL
        UNION ALL
        SELECT list.canon_id AS canon_id
                , this.name_id AS this_id
        FROM list
        JOIN names_org this ON this.parent_id = list.this_id
        )
UPDATE names_org this
SET canon_id = list.canon_id
FROM list
WHERE  this.name_id = list.this_id
        ;

   -- Now we can drop the new name and rename the org name
ALTER TABLE names_org DROP COLUMN  name_new ;
ALTER TABLE names_org RENAME COLUMN  name_org TO current_name ;

SELECT * FROM names_org;

结果:

ALTER TABLE
UPDATE 12
INSERT 0 6
UPDATE 24
ALTER TABLE
ALTER TABLE
 name_id |       current_name        | canon_id | parent_id 
---------+---------------------------+----------+-----------
       1 | Misti Gulick              |        1 |          
       2 | Faye Leaton               |        2 |          
       3 | Arden Peck                |        3 |          
       4 | Carlton Kingsley          |        4 |          
       5 | Dolly Verhey              |        5 |          
       6 | Gaynell Pasquale          |        6 |          
       7 | Misti Gulick Thibodeaux   |        1 |         1
       8 | Faye Leaton Hemby         |        2 |         2
       9 | Arden Peck Mroz           |        3 |         3
      10 | Carlton Kingsley Mcelveen |        4 |         4
      11 | Dolly Verhey Irish        |        5 |         5
      12 | Gaynell Pasquale Ayala    |        6 |         6
      13 | Misti Thibodeaux          |        1 |         7
      14 | Faye Hemby                |        2 |         8
      15 | Arden Mroz                |        3 |         9
      16 | Carlton Mcelveen          |        4 |        10
      17 | Dolly Irish               |        5 |        11
      18 | Gaynell Ayala             |        6 |        12
      19 | Misti Trey Thibodeaux     |        1 |        13
      20 | Faye Barrett Hemby        |        2 |        14
      21 | Arden Justin Mroz         |        3 |        15
      22 | Carlton Tameka Mcelveen   |        4 |        16
      23 | Dolly Jeremiah Irish      |        5 |        17
      24 | Gaynell Cherry Ayala      |        6 |        18
(24 rows)

注意:这个尴尬的结构统一了规范名称/号码(链接列表开始)和更新链(< strong>后向链表),全部合并在一个表中。

可能是更新步骤可以合并在一个语句中,但我不在乎。 而且,正如Erwin评论的那样,这个过程对于错别字,错误命中,不匹配和缺失记录都非常敏感。特别是,charset故障可能非常痛苦。

在大多数情况下,在此过程中的某个地方需要一些手动步骤。

并且,为了完成任务:模拟所需表格的视图:

CREATE VIEW triple_view AS
SELECT
        COALESCE(prev.current_name ,this.current_name) AS name_before
        , this.current_name AS name_after
        ,  abs.current_name AS identifier
FROM names_org this
JOIN names_org prev ON prev.name_id = this.parent_id
JOIN names_org abs ON abs.name_id = this.canon_id
        ;
SELECT * FROM triple_view;

此观点的结果:

        name_before        |        name_after         |    identifier    
---------------------------+---------------------------+------------------
 Misti Gulick              | Misti Gulick Thibodeaux   | Misti Gulick
 Faye Leaton               | Faye Leaton Hemby         | Faye Leaton
 Arden Peck                | Arden Peck Mroz           | Arden Peck
 Carlton Kingsley          | Carlton Kingsley Mcelveen | Carlton Kingsley
 Dolly Verhey              | Dolly Verhey Irish        | Dolly Verhey
 Gaynell Pasquale          | Gaynell Pasquale Ayala    | Gaynell Pasquale
 Misti Gulick Thibodeaux   | Misti Thibodeaux          | Misti Gulick
 Faye Leaton Hemby         | Faye Hemby                | Faye Leaton
 Arden Peck Mroz           | Arden Mroz                | Arden Peck
 Carlton Kingsley Mcelveen | Carlton Mcelveen          | Carlton Kingsley
 Dolly Verhey Irish        | Dolly Irish               | Dolly Verhey
 Gaynell Pasquale Ayala    | Gaynell Ayala             | Gaynell Pasquale
 Misti Thibodeaux          | Misti Trey Thibodeaux     | Misti Gulick
 Faye Hemby                | Faye Barrett Hemby        | Faye Leaton
 Arden Mroz                | Arden Justin Mroz         | Arden Peck
 Carlton Mcelveen          | Carlton Tameka Mcelveen   | Carlton Kingsley
 Dolly Irish               | Dolly Jeremiah Irish      | Dolly Verhey
 Gaynell Ayala             | Gaynell Cherry Ayala      | Gaynell Pasquale
(18 rows)