2016-11-04 1 views
0

Lets dire que j'ai base de données avec une table de mots, comme ceci:requête SQLite retour ordonné coups sensibles à la casse et insensibles à la casse

CREATE TABLE Words (
    Id integer PRIMARY KEY NOT NULL, 
    Word text NOT NULL 
); 
CREATE INDEX Word_Index ON Words (Word ASC); 

sqlite> SELECT * FROM Words; 
Id|Word 
1|apple 
2|Apple 
3|Jack 
4|jack 

Pour garder les choses simples, laisse juste dire que je ne garde à propos des caractères ascii, dans la mesure où la sensibilité de la casse va. Ce que je voudrais faire est d'effectuer une recherche de Word et retourner d'abord la ligne qui renvoie la correspondance exacte sensible à la casse, suivie par toutes les lignes qui correspondent, en ignorant la casse, sans doublons. Ainsi, par exemple, SELECT * FROM mots OU ... 'd'Apple' renverrait:

2|Apple 
1|apple 

et même, SELECT * FROM mots de la recherche ... 'pomme' renverrait:

1|apple 
2|Apple 

Je suis principalement concerné par les correspondances sensibles à la casse, mais souhaiterais qu'elles soient suivies par des correspondances insensibles à la casse, en tant que solution de repli. Je m'attends à ce que généralement j'obtiens des hits pour les correspondances sensibles à la casse, c'est pourquoi j'ai un index sensible à la casse. Je me rends compte que le repli insensible à la casse ne pourra pas utiliser l'index, mais je choisis de ne pas avoir un deuxième index (COLLATE NOCASE) afin d'économiser de l'espace dans ma base de données, car il serait probablement rarement utilisé . Habituellement, je vais seulement marcher une fois, en attrapant le premier coup.

Quel est le moyen le plus efficace de le faire?

Répondre

0

Je pense que vous voulez quelque chose comme ceci:

SELECT * 
FROM Words 
WHERE LOWER(col) = LOWER('Apple') 
ORDER BY (CASE WHEN col = 'Apple' THEN 1 ELSE 2 END), 
     col; 

SQLite est sensible à la casse par défaut.

Vous pouvez essayer, mais je ne sais pas si elle utilise l'index:

SELECT * 
FROM Words 
WHERE col = 'apple' COLLATE NO CASE 
ORDER BY (CASE WHEN col = 'Apple' THEN 1 ELSE 2 END), 
     col; 
+0

Cela semble définitivement fonctionner, mais le plan de requête indique qu'il n'utilise pas l'index: --EQP-- 0,0,0, SCAN TABLE Mots --EQP-- 0,0,0, USE TEMP B-TREE FOR ORDER BY J'aimerais pouvoir utiliser l'index dans les cas où je n'effectue qu'une seule étape pour saisir le premier hit (sensible à la casse), s'il en existe un. – hyperspasm

2

Pour les deux cas sensibles et insensibles à la casse des recherches efficaces, vous avez besoin de deux indices:

CREATE INDEX Word_Index ON Words (Word); 
CREATE INDEX Word_Index_nocase ON Words (Word COLLATE NOCASE); 

Il n'est pas possible d'utiliser des recherches d'index efficaces lorsque vous effectuez des opérations avec ORDER BY; vous devez faire des recherches séparées pour les matches sensibles à la casse et insensible à la casse, et filtrer les doublons du deuxième résultat:

SELECT * 
FROM Words 
WHERE Word = 'Apple' 

UNION ALL 

SELECT * 
FROM Words 
WHERE Word COLLATE NOCASE = 'Apple' 
    AND Word <> 'Apple'; 

(pour gérer les caractères non-ASCII, vous aurez besoin d'installer un classement personnalisé).

+0

Cela fonctionne bien. Si ma compréhension du bytecode de plan de requête (trop grand pour coller ici) est correcte, si j'omets le second index COLLATE NOCASE alors je suis seulement pénalisé pour ne pas l'avoir quand je dépasse les hits sensibles à la casse précédents. Êtes-vous d'accord? – hyperspasm

+1

Oui; SQLite calcule les lignes de résultats uniquement à la demande (autant que possible). –

+0

Bien sûr, vous pouvez générer toutes les combinaisons de lettres possibles de 'aPpLe ', et les rechercher explicitement ... –