2017-07-16 1 views
1

programmées Je ne suis pas habitué à travailler avec des tâches planifiées, j'ai besoin des conseils (est ma pensée bonne ou mauvaise)Gérer la mise à jour d'insertion tâches

Je conçois une fonction qui circule toutes les 20 minutes. Cette fonction récupère les données d'un fichier json (dont je n'ai pas le contrôle) et insère les données dans la base de données. Lorsque je faisais cela, je ne pensais pas que cela créerait un problème d'ID unique dans la base de données que ce sont les mêmes données qui se mettent à jour à chaque fois.

je pensais de faire deux fonctions:

1: les premières insertions (INSERT)

2: Mettre à jour les données selon l'ID (UPDATE)

@Component 
public class LoadSportsCompetition { 

    @PostConstruct 
    public void insert() { 
    // 1 : get json data 
    // 2 : insert in DB 
    } 

    @Scheduled(cron="0 0/20 * * * ?") 
    public void update() { 
    // 1 : get json data 
    // 2 : update rows by ID  
    } 

} 
+0

Quelle est votre question * * réelle? – joanolo

+0

Je suis juste en train de me promener si c'est une bonne ou une mauvaise façon de faire –

+0

Quelle version de PostgreSQL utilisez-vous? Si vous utilisez les versions plus récentes (9.5+), vous voudrez probablement utiliser 'INSERT ... ON CONFLICT DO UPDATE ...;' et effectuer tout en une seule étape. Voir ['INSERT'] (https://www.postgresql.org/docs/current/static/sql-insert.html) – joanolo

Répondre

1

Le (très probablement La meilleure façon de gérer cela dans PostgreSQL 9.5 et les versions ultérieures est d'utiliser INSERT ... ON CONFLICT ... DO UPDATE.

Supposons que ceci est votre table d'origine (très simple, pour des raisons de cet exemple):

CREATE TABLE tbl 
(
    tbl_id INTEGER, 
    payload JSONB, 

    CONSTRAINT tbl_pk 
     PRIMARY KEY (tbl_id) 
) ; 

Nous remplissons avec les données de départ:

INSERT INTO tbl 
    (tbl_id, payload) 
VALUES 
    (1, '{"a":12}'), 
    (2, '{"a":13, "b": 25}'), 
    (3, '{"a":15, "b": [12,13,14]}'), 
    (4, '{"a":12, "c": "something"}'), 
    (5, '{"a":13, "x": 1234.567}'), 
    (6, '{"a":12, "x": 1234.789}') ; 

Maintenant, nous effectuons un non -conflicting insert (ie: la CONFLICT ... ne seront pas exécutées):

-- A normal insert, no conflict 
INSERT INTO tbl 
    (tbl_id, payload) 
VALUES 
    (7, '{"x": 1234.56, "y": 3456.78}') 
ON CONFLICT ON CONSTRAINT tbl_pk DO 
UPDATE 
    SET payload = excluded.payload ; -- Note: the excluded pseudo-table comprises the conflicting rows 

Et maintenant que nous réalisons o ne INSERT qui générerait un conflit PRIMARY KEY, qui sera gérée par la clause ON CONFLICT et effectuera une mise à jour

-- A conflicting insert 
INSERT INTO tbl 
    (tbl_id, payload) 
VALUES 
    (3, '{"a": 16, "b": "I don''t know"}') 
ON CONFLICT ON CONSTRAINT tbl_pk DO 
UPDATE 
    SET payload = excluded.payload ; 

Et maintenant, un insert à deux rangs qui conflit sur une ligne, et insérez l'autre:

-- Now one of each 
-- A conflicting insert 
INSERT INTO tbl 
    (tbl_id, payload) 
VALUES 
    (4, '{"a": 18, "b": "I will we updated"}'), 
    (9, '{"a": 17, "b": "I am nuber 9"}') 
ON CONFLICT ON CONSTRAINT tbl_pk DO UPDATE 
    SET payload = excluded.payload ; 

Nous contrôlons maintenant la table:

SELECT * FROM tbl ORDER BY tbl_id ; 
 
tbl_id | payload        
-----: | :---------------------------------- 
    1 | {"a": 12}       
    2 | {"a": 13, "b": 25}     
    3 | {"a": 16, "b": "I don't know"}  
    4 | {"a": 18, "b": "I will we updated"} 
    5 | {"a": 13, "x": 1234.567}   
    6 | {"a": 12, "x": 1234.789}   
    7 | {"x": 1234.56, "y": 3456.78}  
    9 | {"a": 17, "b": "I am nuber 9"}  

Votre code doit parcourir vos données entrantes, l'obtenir, et exécuter tous les INSERT/UPDATE (parfois appelé MERGE ou UPSERT) une ligne à la fois, ou par lots, avec plusieurs lignes VALUES.

Vous pouvez obtenir tout le code à dbfiddle here


Il y a aussi une alternative, ce qui est mieux adaptée si vous travaillez dans des lots. Utilisez une déclaration WITH, qui a une clause UPDATE, suivie d'une INSERT un:

-- Avoiding (most) concurrency issues. 

BEGIN TRANSACTION ; 
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ; 

WITH data_to_load (tbl_id, payload) AS 
(
    VALUES 
     (3, '{"a": 16, "b": "I don''t know"}' :: jsonb), 
     (4, '{"a": 18, "b": "I will we updated"}'), 
     (7, '{"x": 1234.56, "y": 3456.78}'), 
     (9, '{"a": 17, "b": "I am nuber 9"}') 
), 
update_existing AS 
(
    UPDATE 
     tbl 
    SET 
     payload = data_to_load.payload 
    FROM 
     data_to_load 
    WHERE 
     tbl.tbl_id = data_to_load.tbl_id 
) 
-- Insert the non-existing 
INSERT INTO 
    tbl 
    (tbl_id, payload) 
SELECT 
    tbl_id, payload 
FROM 
    data_to_load 
WHERE 
    data_to_load.tbl_id NOT IN (SELECT tbl_id FROM tbl) ; 

COMMIT TRANSACTION ; 

Vous obtiendrez les mêmes résultats, que vous pouvez voir à dbfiddle here.


Dans les deux cas, être prêt pour le traitement des erreurs et être prêt à recommencer vos transactions si elles entrent en conflit en raison des actions simultanées modifier également votre base de données. Vos transactions peuvent être explicites (comme dans le second cas), ou implicite, si vous avez une sorte d'auto-commit chaque INSERT

+1

Très bonne explication, j'ai fait un tour simple en insérant une seule fois et en mettant à jour après. Je vais probablement intégrer votre solution plus tard –