web-dev-qa-db-fra.com

SQL Insérer dans la table seulement si l'enregistrement n'existe pas

Je souhaite exécuter un ensemble de requêtes pour insérer des données dans une table SQL, mais uniquement si l'enregistrement répondant à certains critères est rempli. La table a 4 champs: id (primaire), fund_id, date et price

J'ai 3 champs dans la requête: fund_id, date et price.

Donc, ma requête aurait quelque chose comme ça:

INSERT INTO funds (fund_id, date, price)
    VALUES (23, '2013-02-12', 22.43)
    WHERE NOT EXISTS (
       SELECT * 
       FROM funds 
       WHERE fund_id = 23
         AND date = '2013-02-12'
    );

Donc, je veux seulement insérer les données si un enregistrement correspondant à fund_id et date n'existe pas déjà. Si ce qui précède est correct, cela me semble être un moyen assez inefficace d'y parvenir, car une instruction select supplémentaire doit être exécutée à chaque fois.

Y a-t-il un meilleur moyen de réaliser ce qui précède?

Edit: Pour plus de précision, ni fund_id ni date ne sont des champs uniques; les enregistrements partageant le même fund_id ou la même date existeront, mais aucun enregistrement ne devrait avoir à la fois le même fund_id et la même date qu'un autre.

63
harryg

Bien que la réponse que j’avais initialement marquée comme étant choisie soit correcte et réponde à ce que j’avais demandé, il existe un meilleur moyen de le faire (ce que d’autres ont reconnu mais n’ont pas abordé). Un index unique composite doit être créé sur la table composée de fund_id et date.

ALTER TABLE funds ADD UNIQUE KEY `fund_date` (`fund_id`, `date`);

Ensuite, lors de l'insertion d'un enregistrement, ajoutez la condition lorsqu'un conflit est rencontré:

INSERT INTO funds (`fund_id`, `date`, `price`)
    VALUES (23, DATE('2013-02-12'), 22.5)
        ON DUPLICATE KEY UPDATE `price` = `price`; --this keeps the price what it was (no change to the table) or:

INSERT INTO funds (`fund_id`, `date`, `price`)
    VALUES (23, DATE('2013-02-12'), 22.5)
        ON DUPLICATE KEY UPDATE `price` = 22.5; --this updates the price to the new value

Cela donnera de bien meilleures performances à une sous-requête et la structure de la table est supérieure. Il est à noter que vous ne pouvez pas avoir de valeur NULL dans vos colonnes de clé uniques car elles sont toujours traitées comme des valeurs par MySQL.

40
harryg

Cela pourrait être une solution simple pour y parvenir:

INSERT INTO funds (ID, date, price)
SELECT 23, DATE('2013-02-12'), 22.5
  FROM dual
 WHERE NOT EXISTS (SELECT 1 
                     FROM funds 
                    WHERE ID = 23
                      AND date = DATE('2013-02-12'));

p.s. alternativement (si ID une clé primaire):

 INSERT INTO funds (ID, date, price)
    VALUES (23, DATE('2013-02-12'), 22.5)
        ON DUPLICATE KEY UPDATE ID = 23; -- or whatever you need

voir ce violon .

57
Trinimon

En supposant que vous ne pouvez pas modifier DDL (pour créer une contrainte unique) ou que vous puissiez uniquement écrire en DML, recherchez un résultat null sur le résultat filtré de vos valeurs par rapport à la table entière.

VIOLON

insert into funds (ID, date, price) 
select 
    T.* 
from 
    (select 23 ID,  '2013-02-12' date,  22.43 price) T  
        left join 
    funds on funds.ID = T.ID and funds.date = T.date
where 
    funds.ID is null
6
gillyspy