【问题标题】:Using COLLATE in Android SQLite - Locales is ignored in LIKE statement在 Android SQLite 中使用 COLLATE - LIKE 语句中忽略了区域设置
【发布时间】:2011-03-29 16:51:43
【问题描述】:

在 Android 中创建我的 SQLite 数据库时,我设置了数据库区域设置 - db.setLocale(new Locale("cz_CZ"))。这是捷克语言环境。

SELECT 语句有效并考虑了语言环境,例如:

SELECT * from table WHERE name='sctzy' COLLATE LOCALIZED 

将找到条目“ščťžý”。

但是使用 LIKE 会失败:

SELECT * from table WHERE name LIKE '%sctzy%' COLLATE LOCALIZED 

没有返回任何行。

顺便说一句。 Android 中没有 java.text.Normalized 类。我想我可以用规范化的文本创建第二列,去掉特殊字符,用于搜索 - 但我缺少如何规范化字符串的类或方法。

【问题讨论】:

  • 我在这里找到了部分答案:stackoverflow.com/questions/3211974/… 但这只是删除重音的方法。我将不得不在我的数据库中使用规范化的文本创建一个重复的列...难道没有更好的方法吗?

标签: android database sqlite locale collate


【解决方案1】:

你看过SQLite documentation for LIKE吗?它提供了有关非 ASCII 字符和错误的信息。也许 Android 安装了旧版本的 SQLite,这是一个问题。

不幸的是,我认为第二个标准化列可能是您的最佳选择。

【讨论】:

  • 如何创建规范化列?
【解决方案2】:

创建第二个规范化列可用于绕过限制(如其他答案中简要提到的)。

这意味着在实践中,您必须在第一个列中创建另一个(阴影)列,其中存储固定大小写的相同数据(例如所有大写字符)。可以使用相同大小写的搜索值对这个新列进行不区分大小写的查询(包括类似查询)。

如果第一列“a”包含

AAA
aaa
Bbb
äää
ééé

第二列 a_shadow 将包含相同的行

AAA
AAA
BBB
ÄÄÄ
ÉÉÉ

以及您的原始查询(示例)“从 mytable 中选择 a='äää'”
将替换为“select a from mytable where A='ÄÄÄ'”

添加主要内容时,您的代码需要更新以填充转换后的阴影内容。 如果列是在创建后添加的,或者您无法更改代码现有值可能需要使用更新查询进行转换。示例:

UPDATE mytable SET a_shadow=UPPER(a);

【讨论】:

    【解决方案3】:

    就在今天,我的任务与你完全相同。在我的情况下,制作额外的阴影列不是一种情况,因为我必须搜索多个列。所以我来到了这样的解决方案,该解决方案在实际项目中进行了测试。在我的情况下,我只处理小写字母,但您也可以使用大写字母扩展功能。

    db.setLocale(Locale("cz", "CZ"))
    val query = "SELECT * FROM table WHERE name GLOB ${getExpr(str)} ORDER BY name COLLATE LOCALIZED ASC"
    
    private fun getExpr(input: String) : String{
        var expr = ""
        for(lettter in input){
            expr += when(lettter){
                's','š' -> "[sš]"
                'a','á' -> "[aá]"
                'e','ě','é' -> "[eěé]"
                'i','í' -> "[ií]"
                'z','ž' -> "[zž]"
                'c','č' -> "[cč]"
                'y','ý' -> "[yý]"
                'r','ř' -> "[rř]"
                'u','ů','ú' -> "[uůú]"
                'o','ó' -> "[oó]"
                'n','ň' -> "[nň]"
                'd','ď' -> "[dď]"
                't','ť' -> "[tť]"
                else -> lettter
            }
         }
         return "'*${expr}*'"
    }
    

    【讨论】:

      【解决方案4】:

      在 Android sqlite 中,LIKEGLOB 忽略 COLLATE LOCALIZEDCOLLATE UNICODE(它们仅适用于 ORDER BY)。但是,正如@asat 在his answer 中解释的那样,您可以使用GLOB 的模式,该模式将用该字母的所有可用替代字母替换每个字母。在 Java 中:

      public static String addTildeOptions(String searchText) {
          return searchText.toLowerCase()
                           .replaceAll("[aáàäâã]", "\\[aáàäâã\\]")
                           .replaceAll("[eéèëê]", "\\[eéèëê\\]")
                           .replaceAll("[iíìî]", "\\[iíìî\\]")
                           .replaceAll("[oóòöôõ]", "\\[oóòöôõ\\]")
                           .replaceAll("[uúùüû]", "\\[uúùüû\\]")
                           .replace("*", "[*]")
                           .replace("?", "[?]");
      }
      

      然后(当然不是字面意思):

      SELECT * from table WHERE lower(column) GLOB "*addTildeOptions(searchText)*"
      

      这样,例如在西班牙语中,搜索 masmás 的用户会将搜索转换为 m[aáàäâã]s,返回两个结果。

      重要的是要注意GLOB 忽略COLLATE NOCASE,这就是为什么我在函数和查询中都将所有内容都转换为小写。另请注意,sqlite 中的 lower() 函数不适用于非 ASCII 字符 - 但这些可能是您已经替换的字符!

      该函数还将GLOB 通配符、*? 替换为“转义”版本。

      【讨论】:

      • 我必须修复你的正则表达式才能让它工作,因为它是倒置的。 .replaceAll("[aáàäâã]", "\\[aáàäâã\\]") 在哪里我不得不使用.replaceAll("\\.*[aáàäâã]\\.*", "[aáàäâã]")
      【解决方案5】:

      可能很耗时,但您可以像这里一样使用 java.text.Normalizer

      Converting Symbols, Accent Letters to English Alphabet

      由于不属于Android的java子集,可以尝试在java的代码中查找,如Normalizer.java 随着 Javadoc 找到 here:

      并复制项目中所需的部分代码。

      希望它有效!

      【讨论】:

        猜你喜欢
        • 2012-12-18
        • 1970-01-01
        • 1970-01-01
        • 2011-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-09
        相关资源
        最近更新 更多