Instruction "Insérer s'il n'existe pas" dans SQLite

143

J'ai une base de données SQLite. J'essaye d'insérer des valeurs ( users_id, lessoninfo_id) dans la table bookmarks, seulement si les deux n'existent pas avant dans une rangée.

INSERT INTO bookmarks(users_id,lessoninfo_id) 
VALUES(
    (SELECT _id FROM Users WHERE User='"+$('#user_lesson').html()+"'),
        (SELECT _id FROM lessoninfo 
        WHERE Lesson="+lesson_no+" AND cast(starttime AS int)="+Math.floor(result_set.rows.item(markerCount-1).starttime)+") 
        WHERE NOT EXISTS (
            SELECT users_id,lessoninfo_id from bookmarks 
            WHERE users_id=(SELECT _id FROM Users 
            WHERE User='"+$('#user_lesson').html()+"') AND lessoninfo_id=(
                SELECT _id FROM lessoninfo
                WHERE Lesson="+lesson_no+")))

Cela donne une erreur disant:

erreur db près de la syntaxe.

user2780638
la source

Réponses:

138

Si vous avez une table appelée mémos qui a deux colonnes idet que textvous devriez pouvoir faire comme ceci:

INSERT INTO memos(id,text) 
SELECT 5, 'text to insert' 
WHERE NOT EXISTS(SELECT 1 FROM memos WHERE id = 5 AND text = 'text to insert');

Si un enregistrement contient déjà une ligne où textest égal à "texte à insérer" et idégal à 5, l'opération d'insertion sera ignorée.

Je ne sais pas si cela fonctionnera pour votre requête particulière, mais cela vous donnera peut-être un indice sur la façon de procéder.

Je vous conseillerais plutôt de concevoir votre table de manière à ce qu'aucun doublon ne soit autorisé, comme expliqué @CLs answerci-dessous.

Cyclonecode
la source
436

Si vous ne voulez jamais avoir de doublons, vous devez le déclarer comme une contrainte de table:

CREATE TABLE bookmarks(
    users_id INTEGER,
    lessoninfo_id INTEGER,
    UNIQUE(users_id, lessoninfo_id)
);

(Une clé primaire sur les deux colonnes aurait le même effet.)

Il est alors possible d'indiquer à la base de données que vous souhaitez ignorer silencieusement les enregistrements qui violeraient une telle contrainte :

INSERT OR IGNORE INTO bookmarks(users_id, lessoninfo_id) VALUES(123, 456)
CL.
la source
12
Quelle est la manière optimale d'obtenir ensuite l'ID de la ligne nouvellement insérée ou de celle déjà existante, afin que je puisse l'insérer dans une autre table qui a une clé étrangère à celle-ci? par exemple j'ai deux tables person (id, name unique)et cat (person_id, name unique)et je veux insérer beaucoup de paires (person_name, cat_name)?
Aur Saraf
2
La lecture de l'ID d'une ligne existante n'est possible qu'avec une requête SELECT. Mais c'est une autre question.
CL.
1
Peut-être ajouter ON CONFLICT IGNOREà CREATE TABLEserait un peu plus à portée de main
defhlt
1
@ rightaway717 "Ignorer" signifie que rien ne se passe; cette question n'a rien à voir avec la mise à jour.
CL.
1
L'insert () d'Android @ Hack06 se comporte différemment; vous devez le remplacer par insertOrThrow () ou insertWithOnConflict () .
CL.
21

Pour une colonne unique, utilisez ceci:

INSERT OR REPLACE INTO table () values();

Pour plus d'informations, voir: sqlite.org/lang_insert

Ali Bagheri
la source
Cela ne fonctionne pas pour moi. il insère toujours comme dans insert ou replace dans les valeurs de ma_table (col1, col2) ('1', '2'); ajoutera plusieurs lignes de 1 2
Peter Moore
Je pourrais ajouter que cela fonctionne bien si la contrainte est mise en place Unique(col1,col2)et que vous avez également col3, car une insertion ou un ignorer échouerait lorsque cela mettra à jour col3 à la nouvelle valeur. insert or replace into my_table values('1','2','5')remplace une ligne'1','2','3'
Peter Moore
4
insert into bookmarks (users_id, lessoninfo_id)

select 1, 167
EXCEPT
select user_id, lessoninfo_id
from bookmarks
where user_id=1
and lessoninfo_id=167;

C'est le moyen le plus rapide.

Pour certains autres moteurs SQL, vous pouvez utiliser une table factice contenant 1 enregistrement. par exemple:

select 1, 167 from ONE_RECORD_DUMMY_TABLE
suat dmk
la source