【问题标题】:sqlite query to return ordered case-sensitive AND case-insensitive hitssqlite 查询返回有序的区分大小写和不区分大小写的命中
【发布时间】:2017-03-17 19:03:24
【问题描述】:

假设我有一个带有单词表的数据库,如下所示:

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 Words WHERE … ‘Apple’ 会返回:

2|Apple
1|apple

同样,SELECT * FROM Words WHERE … ‘apple’ 会返回:

1|apple
2|Apple

我主要关心区分大小写的匹配,但希望它们后面跟着不区分大小写的匹配,作为后备。我希望通常我会在区分大小写的匹配中获得命中,这就是为什么我有一个区分大小写的索引。我意识到不区分大小写的回退将无法使用索引,但我选择不使用第二个(COLLATE NOCASE)索引以节省数据库空间,因为无论如何它可能不会经常使用.通常我只会踩一次,抓住第一击。

最有效的方法是什么?

【问题讨论】:

    标签: sql sqlite collation


    【解决方案1】:

    为了使区分大小写和不区分大小写的搜索都高效,您需要两个索引:

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

    当您使用 ORDER BY 进行技巧时,不可能使用有效的索引查找;您必须分别查找区分大小写和不区分大小写的匹配项,并从第二个结果中过滤掉重复项:

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

    (要处理非 ASCII 字符,您需要安装自定义排序规则)。

    【讨论】:

    • 这很好用。如果我对查询计划字节码(太大而无法粘贴到此处)的理解是正确的,如果我省略了第二个 COLLATE NOCASE 索引,那么当我超出前面区分大小写的命中时,我只会因为没有它而受到惩罚。你同意吗?
    • 是的; SQLite 仅按需计算结果行(尽可能)。
    • 当然,您可以生成所有可能的不区分大小写的aPpLe 的字母组合,并显式搜索它们……
    • 回到这一点,使用UNION 而不是UNION ALL 这样可以跳过AND Word &lt;&gt; 'Apple' 不是更有意义吗?我错过了什么吗?
    • UNION 将所有结果放入临时索引(B 树)中,以便能够检测重复项。只检查一个值是否不是“Apple”会更快,因为它不需要与其他行进行比较。
    【解决方案2】:

    我想你想要这样的东西:

    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;
    

    【讨论】:

    • 这似乎确实有效,但查询计划表明它没有使用索引:--EQP-- 0,0,0,SCAN TABLE Words --EQP-- 0,0,0 ,USE TEMP B-TREE FOR ORDER BY 我希望能够在我只踩一次以获取第一个(区分大小写)命中的情况下使用索引,当一个存在时。
    猜你喜欢
    • 2013-03-06
    • 2013-04-25
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多