PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

PostgreSQL - insira linhas com base na seleção de outra tabela e atualize um FK nessa tabela com as linhas recém-inseridas


Existem várias maneiras de resolver o problema.

1. adicionar temporariamente uma coluna

Como outros mencionaram, a maneira mais direta é adicionar temporariamente uma coluna reminder_id para o dateset . Preencha-o com IDs originais de reminder tabela. Use-o para participar do reminder com o dateset tabela. Solte a coluna temporária.

2. quando o início é único

Se os valores do start coluna é única é possível fazê-lo sem coluna extra juntando reminder tabela com o dateset tabela no start coluna.
INSERT INTO dateset (start)
SELECT start FROM reminder;

WITH
CTE_Joined
AS
(
    SELECT
        reminder.id AS reminder_id
        ,reminder.dateset_id AS old_dateset_id
        ,dateset.id AS new_dateset_id
    FROM
        reminder
        INNER JOIN dateset ON dateset.start = reminder.start
)
UPDATE CTE_Joined
SET old_dateset_id = new_dateset_id
;

3. quando o início não é exclusivo

É possível fazê-lo sem coluna temporária mesmo neste caso. A ideia principal é a seguinte. Vamos dar uma olhada neste exemplo:

Temos duas linhas em reminder com o mesmo start valor e IDs 3 e 7:
reminder
id    start         dateset_id
3     2015-01-01    NULL
7     2015-01-01    NULL

Depois de inseri-los no dateset , serão gerados novos IDs, por exemplo, 1 e 2:
dateset
id    start
1     2015-01-01
2     2015-01-01

Não importa realmente como vinculamos essas duas linhas. O resultado final pode ser
reminder
id    start         dateset_id
3     2015-01-01    1
7     2015-01-01    2

ou
reminder
id    start         dateset_id
3     2015-01-01    2
7     2015-01-01    1

Ambas as variantes estão corretas. O que nos leva à seguinte solução.

Basta inserir todas as linhas primeiro.
INSERT INTO dateset (start)
SELECT start FROM reminder;

Combine/junte duas tabelas em start coluna sabendo que ela não é única. "Torne-o" exclusivo adicionando ROW_NUMBER e unindo por duas colunas. É possível tornar a consulta mais curta, mas expliquei cada etapa explicitamente:
WITH
CTE_reminder_rn
AS
(
    SELECT
        id
        ,start
        ,dateset_id
        ,ROW_NUMBER() OVER (PARTITION BY start ORDER BY id) AS rn
    FROM reminder
)
,CTE_dateset_rn
AS
(
    SELECT
        id
        ,start
        ,ROW_NUMBER() OVER (PARTITION BY start ORDER BY id) AS rn
    FROM dateset
)
,CTE_Joined
AS
(
    SELECT
        CTE_reminder_rn.id AS reminder_id
        ,CTE_reminder_rn.dateset_id AS old_dateset_id
        ,CTE_dateset_rn.id AS new_dateset_id
    FROM
        CTE_reminder_rn
        INNER JOIN CTE_dateset_rn ON 
            CTE_dateset_rn.start = CTE_reminder_rn.start AND
            CTE_dateset_rn.rn = CTE_reminder_rn.rn
)
UPDATE CTE_Joined
SET old_dateset_id = new_dateset_id
;

Espero que fique claro no código o que ele faz, especialmente quando você o compara com a versão mais simples sem ROW_NUMBER . Obviamente, a solução complexa funcionará mesmo se start é único, mas não é tão eficiente, como uma solução simples.

Esta solução assume que dateset está vazio antes deste processo.