2017-01-19 40 views
0

我有兩個表PostgreSQL模式:更新的Postgres表壁球重複的值在第二個表

tableA:      tableB: 

| id | username |   | fk_id | resource | 
| 1 | user1 |   | 2  | item1 | 
| 2 | user1 |   | 1  | item3 | 
| 3 | user1 |   | 1  | item2 | 
| 4 | user2 |   | 4  | item5 | 
| 5 | user2 |   | 5  | item8 | 
| 6 | user3 |   | 3  | item9 | 

外鍵fk_id TableB中TableA中引用id

如何更新tableB的所有外鍵ID以指向tableA中唯一用戶名的最低項?

回答

1
update table_b b 
set fk_id = d.id 
from table_a a 
join (
    select distinct on (username) username, id 
    from table_a 
    order by 1, 2 
    ) d using(username) 
where a.id = b.fk_id; 

Test it here.

更新內部使用的查詢提供了actual_id, username, desired_id

select a.id actual_id, username, d.id desired_id 
from table_a a 
join (
    select distinct on (username) username, id 
    from table_a 
    order by 1, 2 
    ) d using(username) 

actual_id | username | desired_id 
-----------+----------+------------ 
     1 | user1 |   1 
     2 | user1 |   1 
     3 | user1 |   1 
     4 | user2 |   4 
     5 | user2 |   4 
     6 | user3 |   6 
(6 rows)  
1

我們定義你的表:

CREATE TABLE tableA (id, username) AS 
SELECT * FROM 
(
    VALUES 
    (1, 'user1'), 
    (2, 'user1'), 
    (3, 'user1'), 
    (4, 'user2'), 
    (5, 'user2'), 
    (6, 'user2') 
) AS x ; 

CREATE TABLE tableB (fk_id, resource) AS 
SELECT * FROM 
(
    VALUES 
    (2, 'item1'), 
    (1, 'item3'), 
    (1, 'item2'), 
    (4, 'item5'), 
    (5, 'item8'), 
    (3, 'item9') 
) AS x ; 

有了這些信息,你可以創建一個(虛擬)轉換表,並用它來更新你的數據:

-- Using tableA, make a new table with the 
-- minimum id for every username 
WITH username_to_min_id AS 
(
SELECT 
    min(id) AS min_id, username 
FROM 
    tableA 
GROUP BY 
    username 
) 

-- Convert the previous table to a id -> min_id 
-- conversion table 
, id_to_min_id AS 
(
SELECT 
    id, min_id 
FROM 
    tableA 
    JOIN username_to_min_id USING(username) 
) 

-- Use this conversion table to update tableB 
UPDATE 
    tableB 
SET 
    fk_id = min_id 
FROM 
    id_to_min_id 
WHERE 
    -- JOIN condition with table to update 
    id_to_min_id.id = tableB.fk_id 
    -- Take out the ones that won't change 
    AND (fk_id <> min_id) 
RETURNING 
    * ; 

,你會得到的結果是:

+-------+----------+----+--------+ 
| fk_id | resource | id | min_id | 
+-------+----------+----+--------+ 
|  1 | item1 | 2 |  1 | 
|  1 | item9 | 3 |  1 | 
|  4 | item8 | 5 |  4 | 
+-------+----------+----+--------+ 

您顯示三行已被更新,即有fk_id =(2,3,5),和現在有(1,1, 4)。 (id是「舊」fk_id值)。

您可以在http://rextester.com/EQPH47434


檢查,你可以「擠一切」 [由它的定義改變每個虛擬表的名稱,並做了幾個SELECT優化],並得到這個等效的查詢(可能不太清楚,但完全等同):

UPDATE 
    tableB 
SET 
    fk_id = min_id 
FROM 
    tableA 
    JOIN 
    (
    SELECT 
     min(id) AS min_id, username 
    FROM 
     tableA 
    GROUP BY 
     username 
) AS username_to_min_id 
    USING (username) 
WHERE 
    tableA.id = tableB.fk_id 
    AND (fk_id <> min_id) 
RETURNING 
    * ; 
相關問題