【问题标题】:SQL LIKE query: escape wildcardsSQL LIKE 查询:转义通配符
【发布时间】:2017-06-28 18:19:57
【问题描述】:

我在 Android Uri 的列上运行了一个 LIKE 查询,其格式如下:

  1. content://com.android.externalstorage.documents/tree/0000-0000%3A_Issues/document/0000-0000%3A_Issues%2FParentB
  2. content://com.android.externalstorage.documents/tree/0000-0000%3A_Issues/document/0000-0000%3A_Issues%2FParentA
  3. content://com.android.externalstorage.documents/tree/0000-0000%3A_Issues/document/0000-0000%3A_Issues%2FParentA%2F 父母B

这些对应于这个文件树:

_Issues
|-ParentA
   |-ParentB
|-ParentB

我尝试了各种手动查询来转义通配符,但我似乎无法获得正确的匹配。我将其简化为测试:

select name from meta where parent LIKE '%_Issues%2FParentB%';
  1. 如果我转义 '%':

%_Issues\%2FParentB%'

我没有得到任何结果。

  1. 如果我不逃跑:

'%_Issues%2FParentB%'

我同时匹配浅 ParentB(期望)和嵌套 ParentB(不期望)。我明白那是因为我允许在 Issues 和 ParentB 之间使用 %.

我不明白为什么查询 1 没有结果。我错过了什么?


更新

select name from meta where parent LIKE '%_Issues_2FParentB%';

工作,注意'_',所以'\'显然没有逃脱这个数据库。在我的电脑上明确说明@GordonLinoff 建议的转义字符:

select name from meta where parent LIKE '%_Issues\%2FParentB%' escape '\';

现在来看看我们如何在 Android 中完成同样的事情......

【问题讨论】:

  • 编辑您的问题并显示您正在使用的代码。
  • 已编辑,由于我缺乏知识,这最终比我预期的要复杂得多。

标签: java android sql sql-like


【解决方案1】:

为了扩展@GordonLinoff 的答案,以下是我在 Android 中的具体做法,因为它稍微复杂一些:

/***
 * Creates a LIKE selection statement with all of the given arguments
 * @param column column to select on
 * @param likes array of arguments to select on
 * @param selectionArgs out: formatted arguments to pass to query
 * @param joiner joiner between individual LIKE
 * @param NOT true to set NOT LIKE for all selection arguments
 * @param argStart set a wildcard before every selection argument
 * @param argEnd set a wildcard after every selection argument
 * @return selection statement to query
 */
public static String createLike(String column, String[] likes, List<String> selectionArgs,
                                String joiner, boolean NOT,
                                @Nullable String argStart, @Nullable String argEnd,
                                @Nullable String escapeChar)
{
    StringBuilder selection = new StringBuilder();
    for (int i = 0; i < likes.length; i++)
    {
        if (i > 0) selection.append(joiner);

        if (argStart == null)
            argStart = "";
        if (argEnd == null)
            argEnd = "";

        selection.append(column)
                .append(NOT ? " NOT LIKE ?" : " LIKE ?");

        if (escapeChar != null)
            selection.append(" ESCAPE '\\'");

        String argument = likes[i];
        if (escapeChar != null)
            argument = argument.replace(escapeChar, "\\" + escapeChar);
        argument = argStart + argument + argEnd;
        selectionArgs.add(argument);
    }

    return selection.toString();
}

并调用:

DbUtil.createLike(Meta.PARENT,
                    filter.hiddenFolders.toArray(new String[filter.hiddenFolders.size()]),
                    selectionArgs,
                    AND,    // Requires AND so multiple hides don't negate each other
                    true,   // NOT
                    null,   // No wild to start, matches path exactly
                    "%",    // Wildcard end to match all children
                    "%"));  // Uri contain '%' which means match any so escape them

【讨论】:

    【解决方案2】:

    是的。最简单的方法是使用=,如果合适的话。

    否则,LIKE 语法包含转义字符。在您的示例中,$ 没有出现在任何地方,因此您可以这样做:

    where x like replace(y, '%', '$%') escape '$'
    

    我相信在所有数据库中,默认都是\,所以你也可以这样做:

    where x like replace(y, '%', '\%') 
    

    不过,我更喜欢使用 escape 关键字,因此意图非常明确。

    【讨论】:

    • 应该是:where x like replace(y, '%', '\%'),对吧?匹配 % 不是 \.
    • MySQL 和 PostgreSQL 有一个默认值,但至少在 Oracle、SQL Server 和 Teradata 中没有默认转义字符(符合标准 SQL)。 SQL 不是一种编程语言,恕我直言,默认设置是愚蠢的,因为它在 _% 之外添加了第三个具有特殊含义的字符,如果不转义它或更改转义字符,您将无法搜索 ``。跨度>
    • @dnoeth 。 . .无论如何,如果我需要使用escape,我更喜欢显式形式。
    • 在这种情况下,转义是必要的。 Android似乎没有默认值。我在下面添加了必要的 Android 代码。