Contrainte de table SQLite - unique sur plusieurs colonnes

179

Je peux trouver des «graphiques» de syntaxe à ce sujet sur le site Web de SQLite, mais aucun exemple et mon code plante. J'ai d'autres tables avec des contraintes uniques sur une seule colonne, mais je souhaite ajouter une contrainte à la table sur deux colonnes. C'est ce que j'ai qui provoque une exception SQLiteException avec le message "erreur de syntaxe".

CREATE TABLE name (column defs) 
UNIQUE (col_name1, col_name2) ON CONFLICT REPLACE

Je fais cela en fonction de ce qui suit:

contrainte de table

Pour être clair, la documentation sur le lien que j'ai fourni indique que cela CONTSTRAINT namedevrait précéder ma définition de contrainte.

Quelque chose qui peut conduire à la solution est que tout ce qui suit mes définitions de colonnes entre parenthèses est ce dont le débogueur se plaint.

Si je mets

...last_column_name last_col_datatype) CONSTRAINT ...

l'erreur est proche de "CONSTRAINT": erreur de syntaxe

Si je mets

...last_column_name last_col_datatype) UNIQUE ...

l'erreur est proche de "UNIQUE": erreur de syntaxe

Riches
la source
1
UNIQUE manque une virgule avant de commencer ..
Majid Bashir

Réponses:

345

Placez la déclaration UNIQUE dans la section de définition de colonne; exemple de travail:

CREATE TABLE a (
    i INT,
    j INT,
    UNIQUE(i, j) ON CONFLICT REPLACE
);
Ayman Hourieh
la source
6
Belle réponse +1. Cette syntaxe de création me permet-elle d'utiliser la méthode d'insertion régulière, et non insertWithOnConflict avec l'indicateur SQLiteDatabase.CONFLICT_REPLACE?
Oleg Belousov
3
J'utilise (je ON CONFLICT IGNOREn'ai pas encore essayé de remplacer) avec plus de 2 colonnes, mais je ne le vois pas honorer la contrainte unique, il ajoute juste joyeusement les doublons.
Michael
5
apparemment parce que j'ai des colonnes NULL, et cela ne fait que tirer l'unique vérification par la fenêtre
Michael
Méfiez-vous de l'utiliser ON CONFLICT REPLACEpeut ne pas être ce que vous voulez - il supprime les lignes préexistantes pour permettre à la nouvelle ligne d'être insérée. Normalement, je voudrais ABORT ou ROLLBACK la violation de contrainte. Clause SQLite ON CONFLICT
karmakaze
9

Eh bien, votre syntaxe ne correspond pas au lien que vous avez inclus, qui spécifie:

 CREATE TABLE name (column defs) 
    CONSTRAINT constraint_name    -- This is new
    UNIQUE (col_name1, col_name2) ON CONFLICT REPLACE
Larry Lustig
la source
J'ai d'abord fait ça ... ça n'a pas marché. Je l'ai réessayé au cas où ... ne fonctionne toujours pas
Rich
1

Faites attention à la façon dont vous définissez la table car vous obtiendrez des résultats différents lors de l'insertion. Considérer ce qui suit



CREATE TABLE IF NOT EXISTS t1 (id INTEGER PRIMARY KEY, a TEXT UNIQUE, b TEXT);
INSERT INTO t1 (a, b) VALUES
    ('Alice', 'Some title'),
    ('Bob', 'Palindromic guy'),
    ('Charles', 'chucky cheese'),
    ('Alice', 'Some other title') 
    ON CONFLICT(a) DO UPDATE SET b=excluded.b;
CREATE TABLE IF NOT EXISTS t2 (id INTEGER PRIMARY KEY, a TEXT UNIQUE, b TEXT, UNIQUE(a) ON CONFLICT REPLACE);
INSERT INTO t2 (a, b) VALUES
    ('Alice', 'Some title'),
    ('Bob', 'Palindromic guy'),
    ('Charles', 'chucky cheese'),
    ('Alice', 'Some other title');

$ sqlite3 test.sqlite
SQLite version 3.28.0 2019-04-16 19:49:53
Enter ".help" for usage hints.
sqlite> CREATE TABLE IF NOT EXISTS t1 (id INTEGER PRIMARY KEY, a TEXT UNIQUE, b TEXT);
sqlite> INSERT INTO t1 (a, b) VALUES
   ...>     ('Alice', 'Some title'),
   ...>     ('Bob', 'Palindromic guy'),
   ...>     ('Charles', 'chucky cheese'),
   ...>     ('Alice', 'Some other title') 
   ...>     ON CONFLICT(a) DO UPDATE SET b=excluded.b;
sqlite> CREATE TABLE IF NOT EXISTS t2 (id INTEGER PRIMARY KEY, a TEXT UNIQUE, b TEXT, UNIQUE(a) ON CONFLICT REPLACE);
sqlite> INSERT INTO t2 (a, b) VALUES
   ...>     ('Alice', 'Some title'),
   ...>     ('Bob', 'Palindromic guy'),
   ...>     ('Charles', 'chucky cheese'),
   ...>     ('Alice', 'Some other title');
sqlite> .mode col
sqlite> .headers on
sqlite> select * from t1;
id          a           b               
----------  ----------  ----------------
1           Alice       Some other title
2           Bob         Palindromic guy 
3           Charles     chucky cheese   
sqlite> select * from t2;
id          a           b              
----------  ----------  ---------------
2           Bob         Palindromic guy
3           Charles     chucky cheese  
4           Alice       Some other titl
sqlite> 

Bien que l'effet d'insertion / mise à jour soit le même, les idchangements basés sur le type de définition de table (voir le deuxième tableau où `` Alice '' a maintenant id = 4; la première table fait plus de ce que j'attends d'elle, gardez la clé primaire la même ). Soyez conscient de cet effet.

punk
la source
1

Si vous avez déjà une table et que vous ne pouvez / ne voulez pas la recréer pour une raison quelconque, utilisez des index :

CREATE UNIQUE INDEX my_index ON my_table(col_1, col_2);
Oleg Yablokov
la source