PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

PostgreSQL - insère des lignes en fonction de la sélection d'une autre table et met à jour un FK dans cette table avec les lignes nouvellement insérées

Il existe plusieurs façons de résoudre le problème.

1. ajouter temporairement une colonne

Comme d'autres l'ont mentionné, la méthode la plus simple consiste à ajouter temporairement une colonne reminder_id au dateset . Remplissez-le avec les IDs d'origine de reminder table. Utilisez-le pour rejoindre reminder avec le dateset table. Supprimez la colonne temporaire.

2. quand le début est unique

Si les valeurs du start colonne est unique il est possible de le faire sans colonne supplémentaire en joignant reminder table avec le dateset table sur le start colonne.

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. quand le début n'est pas unique

Il est possible de le faire sans colonne temporaire même dans ce cas. L'idée principale est la suivante. Examinons cet exemple :

Nous avons deux lignes dans reminder avec le même start valeur et identifiants 3 et 7 :

reminder
id    start         dateset_id
3     2015-01-01    NULL
7     2015-01-01    NULL

Après les avoir insérés dans le dateset , de nouveaux identifiants seront générés, par exemple, 1 et 2 :

dateset
id    start
1     2015-01-01
2     2015-01-01

Peu importe comment nous relions ces deux lignes. Le résultat final pourrait être

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

Ces deux variantes sont correctes. Ce qui nous amène à la solution suivante.

Insérez simplement toutes les lignes en premier.

INSERT INTO dateset (start)
SELECT start FROM reminder;

Faire correspondre/joindre deux tables sur start colonne sachant qu'elle n'est pas unique. "Rendez-le" unique en ajoutant ROW_NUMBER et joint par deux colonnes. Il est possible de raccourcir la requête, mais j'ai expliqué chaque étape explicitement :

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
;

J'espère que le code indique clairement ce qu'il fait, en particulier lorsque vous le comparez à la version plus simple sans ROW_NUMBER . Évidemment, la solution complexe fonctionnera même si start est unique, mais pas aussi efficace qu'une solution simple.

Cette solution suppose que dateset est vide avant ce processus.