SQLite INSERT - SUR LA MISE À JOUR DE LA CLÉ DUPLICATE (UPSERT)

98

MySQL a quelque chose comme ceci:

INSERT INTO visits (ip, hits)
VALUES ('127.0.0.1', 1)
ON DUPLICATE KEY UPDATE hits = hits + 1;

Autant que je sache, cette fonctionnalité n'existe pas dans SQLite, ce que je veux savoir, c'est s'il existe un moyen d'obtenir le même effet sans avoir à exécuter deux requêtes. Aussi, si ce n'est pas possible, que préférez-vous:

  1. SELECT + (INSERT ou UPDATE) ou
  2. UPDATE (+ INSERT si UPDATE échoue )
Alix Axel
la source

Réponses:

31

Depuis 3.24.0, SQLite prend également en charge upsert , vous pouvez donc simplement écrire ce qui suit

INSERT INTO visits (ip, hits)
VALUES ('127.0.0.1', 1)
ON CONFLICT(ip) DO UPDATE SET hits = hits + 1;
szmate1618
la source
3
Je me demandais si vous pouviez faire plusieurs upsertcomme ça en une seule transaction, c'est-à-dire avec la executemany()fonction Python ?
Radiocommandé le
117
INSERT OR IGNORE INTO visits VALUES ($ip, 0);
UPDATE visits SET hits = hits + 1 WHERE ip LIKE $ip;

Cela nécessite que la colonne "ip" ait une contrainte UNIQUE (ou PRIMARY KEY).


EDIT: Une autre excellente solution: https://stackoverflow.com/a/4330694/89771 .

dan04
la source
2
Pour mémoire, ce REPLACEn'est pas une option.
Alix Axel
1
En ce qui concerne le lien "une autre excellente solution", je considérerais également une réponse différente à la même question: stackoverflow.com/a/418988/3650835
KayakinKoder
19

Je préférerais UPDATE (+ INSERT if UPDATE fails). Moins de code = moins de bogues.

codéholique
la source
1
Merci! @Sam ( stackoverflow.com/questions/418898/… ) semble être d'accord avec vous. Je préfère également cette approche.
Alix Axel
@Smith Je voulais dire utiliser des instructions UPDATE et INSERT simples et vérifier la valeur de retour.
codeholic
Cela n'a pas d'atomicité, il est possible que INSERT échoue si un autre processus est inséré entre les deux.
Robin Lavallée
7

La réponse actuelle ne fonctionnera que dans sqlite OU mysql (selon que vous utilisez OU ou non). Donc, si vous voulez une compatibilité cross dbms, ce qui suit fera l'affaire ...

REPLACE INTO `visits` (ip, value) VALUES ($ip, 0);
Jacob Thomason
la source
3
La réponse acceptée fonctionne sur SQLite (c'était mon objectif). REPLACEfonctionnera également sur SQLite, mais sur MySQL, il réinitialisera toujours le compteur à 0 - alors que la requête sera portable, le résultat final sera très différent.
Alix Axel
Vous avez raison, je pensais que l'OP cherchait quelque chose de portable. Je me rends compte que REPLACE INTO ne fonctionnera pas avec tous les cas, en particulier lorsque la préservation PK est nécessaire, mais le sera dans de nombreux cas.
Jacob Thomason
Échouer proprement au lieu de supprimer des données est une fonctionnalité, pas un bogue.
Tobu
-4

Vous devez utiliser memcached pour cela car il s'agit d'une seule clé (l'adresse IP) stockant une seule valeur (le nombre de visites). Vous pouvez utiliser la fonction d'incrémentation atomique pour vous assurer qu'il n'y a pas de conditions de «course».

C'est plus rapide que MySQL et économise la charge afin que MySQL puisse se concentrer sur d'autres choses.

Xeoncross
la source
Si les données ne sont pas si importantes, oui. Cependant, si cela est utilisé sur un site occupé où de nombreuses adresses IP atteignent le service, la ou les instances Memcached peuvent être saturées et entraîner la suppression de certains contenus. La sauvegarde du contenu memcached serait également intéressante (si nécessaire.)
Elliot Foster
@ElliotFoster Memcached peut gérer autant de données que la RAM que vous lui lancez (si vous voulez également de la persistance, utilisez redis ou membase). Si vous recevez plus d'un million de visiteurs par jour, vous pouvez probablement vous permettre de donner à votre instance Memcache plus de 30 Mo de RAM (ce qui est la valeur par défaut je pense). Cependant, il peut certainement gérer une charge beaucoup plus élevée que SQLite et MySQL pour la quantité de mémoire que vous lui donnez - il n'y a tout simplement pas de comparaison.
Xeoncross
Ne confondez pas mon commentaire avec un vote contre Memcache, car je pense que c'est un outil fantastique. Comme redis (je ne peux pas parler pour membase, car je ne l'ai pas utilisé.) Cependant, memcache / redis ne sont pas les magasins les plus fiables. Oui, redis a la persistance, mais les données sont conservées sur le disque sur un intervalle (la dernière fois que j'ai regardé) et Memcache pas du tout. Comme je l'ai dit, si les données ne sont pas importantes (ou peuvent être reproduites facilement), Memcache et l'entreprise sont excellentes. Le message original posait également des questions sur sqlite, qui est très différent de MySQL et signifie probablement qu'ils sont limités par d'autres moyens.
Elliot Foster
Vous pouvez configurer Redis pour conserver les données aussi rapidement que vous le souhaitez (à X nombre de secondes ou à Y nombre de changements).
Buffalo