以下列表是指结婚前后的人名。随着时间的推移,他们中的一些人离婚,再次结婚和/或改名 我想要做的是获取该人一生中所有的名字,并为每个人添加一个带有唯一标识符的新列。
这是名为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;
我知道这根本不优雅,没有表现。你们中的一些人会帮我改进吗?或者甚至欢迎其他建议!!谢谢,
答案 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
使用上述架构,您可以使用单个查询执行所有内容:填写新表并返回结果:
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 *
最后,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)