2016-11-04 8 views
0

Позволяет сказать у меня есть база данных с таблицей слов, например:SQLite запроса на возвращение упорядоченных регистрозависимые И регистронезависимые хиты

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 

Для простоты, давайте просто сказать, что я только уход о символах ascii, насколько чувствительна к регистру.

Что я хотел бы сделать, это выполнить поиск Word и сначала вернуть строку, которая возвращает точное совпадение с регистром, а затем все строки, которые соответствуют, игнорируя случай, без дубликатов. Так, например, SELECT * FROM слова WHERE ... Apple, ' возвращал:

2|Apple 
1|apple 

и также, SELECT * FROM слова WHERE ... яблоко' вернется:

1|apple 
2|Apple 

В первую очередь я интересуюсь совпадениями, чувствительными к регистру, но хотел бы, чтобы за ними последовали нечувствительные к регистру совпадения, как резерв. Я ожидаю, что, как правило, я получаю хиты для аргументов, чувствительных к регистру, поэтому у меня есть регистр, чувствительный к регистру. Я понимаю, что нечувствительность к регистру не сможет использовать индекс, но я предпочитаю не иметь второго (COLLATE NOCASE) индекса, чтобы сэкономить место в моей базе данных, поскольку он, вероятно, будет использоваться редко в любом случае , Обычно я собираюсь только шагнуть, захватив первый удар.

Каков наиболее эффективный способ сделать это?

ответ

0

Я думаю, что вы хотите что-то вроде этого:

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

SQLite чувствителен к регистру по умолчанию.

Вы можете попробовать это, но я не знаю, если он будет использовать индекс:

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

Это определенно работает, но план запроса указывает, что он не использует индекс: --EQP-- 0,0,0, SCAN TABLE Words --EQP-- 0,0,0, USE TEMP B-TREE FOR ORDER BY Я бы хотел использовать индекс в тех случаях, когда я делаю шаг только один раз, чтобы захватить первый (чувствительный к регистру) удар, когда он существует. – hyperspasm

2

Для того, чтобы как чувствительны к регистру и нечувствительные к регистру поиск эффективной, вам необходимо два индекса:

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

Невозможно использовать эффективные поисковые запросы, когда вы делаете трюки с помощью ORDER BY; Вы должны сделать отдельную Lookups для чувствительны к регистру и не чувствительны к регистру спичек и отфильтровать дубликаты из второго результата:

SELECT * 
FROM Words 
WHERE Word = 'Apple' 

UNION ALL 

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

(Для обработки не-ASCII символов, вам нужно установить пользовательские параметры сортировки).

+0

Это работает красиво. Если мое понимание байт-кода плана запроса (слишком большое для вставки здесь) является правильным, если я опускаю второй индекс COLLATE NOCASE, тогда я буду наказан только за то, что не получил его, когда я выхожу за пределы предыдущих чувствительных к регистру обращений. Вы согласны? – hyperspasm

+1

Да; SQLite вычисляет результирующие строки только по требованию (насколько это возможно). –

+0

Конечно, вы могли бы генерировать все возможные комбинации букв без учета регистра 'aPpLe' и искать их все явно ... –