忽略 DB2 中空格的有效方法?

Efficient way to ignore whitespace in DB2?

我 运行 在一个大型 IBM DB2 数据库中进行查询 table(我们称之为 T)并且发现列标识符的单元格往往不仅在边距处被填充,而且也介于两者之间,如:' ID1 ID2 '。我无权更新此数据库,考虑到许多因素,我也不会。但是,我想要一种方法来至少忽略左右两侧的空格,即使我需要简单地在它们之间添加几个空格也是如此。以下查询 有效 ,但速度很慢,超过 20 秒....

SELECT * FROM T WHERE Identifier LIKE '%ID1%ID2%';
SELECT * FROM T WHERE TRIM(Identifier) LIKE 'ID1%ID2';
SELECT * FROM T WHERE TRIM(Identifier) = 'ID1  ID2';
SELECT * FROM T WHERE LTRIM(RTRIM(Identifier)) = 'ID1  ID2';
SELECT * FROM T WHERE LTRIM(Identifier) LIKE 'ID1  ID2%';
SELECT * FROM T WHERE LTRIM(Identifier) LIKE 'ID1%ID2%';
SELECT * FROM T WHERE RTRIM(Identifier) LIKE '%ID1  ID2';
SELECT * FROM T WHERE RTRIM(Identifier) LIKE '%ID1%ID2';

尝试查询类似“Select * FROM T WHERE REPLACE(Identifier, ' ', '')...”之类的内容时,当然只会冻结 Access,直到我按 Ctrl+Break 结束操作。有没有更好、更有效的方法来忽略空格?

==================================

更新: 正如@Paul Vernon 在下面描述的那样,“出于比较目的,Db2 中忽略尾随空格,因此您只需要考虑前导空格和嵌入空格。”

这导致我使用 IN 子句在 'ID1' 和 'ID2' 和 select 记录之前生成空格组合。组合的数量意味着查询比我知道完全匹配要慢。这就是它在我的 Java 代码中的样子 Jdbc (经过编辑以使其对关键问题更通用):

    private static final int MAX_LENGTH = 30;

    public List<Parts> queryMyTable(String ID1, String ID2) {
        String query="SELECT * FROM MYTABLE WHERE ID IN (:ids)";
        final Map<String, List<String>> parameters = getIDCombinations(ID1, ID2);
        return namedJdbcTemplate.query(query,parameters,new PartsMapper());
    }


    public static List<String> getIDCombinations(String ID1, String ID2) {
        List<String> combinations = new ArrayList<>();
        final int literalLength = ID1.length() + ID2.length();
        final int maxWhitespace = MAX_LENGTH - literalLength;
        combinations.add(ID1+ID2);
        for(int x = 1; x <= maxWhitespace; x++){
            String xSpace = String.format("%1$"+x+"s", "");
            String idZeroSpaceBeforeBase = String.format("%s%s%s",ID1,xSpace,ID2);
            String idZeroSpaceAfterBase = String.format("%s%s%s",xSpace,ID1,ID2);
            combinations.add(idZeroSpaceBeforeBase);
            combinations.add(idZeroSpaceAfterBase);
            for(int y = 1; (x+y) <= maxWhitespace; y++){
                String ySpace = String.format("%1$"+y+"s", "");
                String id = String.format("%s%s%s%s",xSpace,ID1,ySpace,ID2);
                combinations.add(id);
            }
        }
        return combinations;
    }

如果您需要搜索排除前导和尾随空格,那么没有任何传统索引可以帮助您,至少在您展示的情况下。为了使查询更快,我可以看到的选项是:

全文搜索

您可以使用“全文搜索”解决方案。 DB2 确实包含此功能,但我不记得它是默认包含在许可证中还是单独出售。在任何情况下,都需要对数据进行一些索引或定期 re-indexing 以确保搜索是最新的。如果你真的需要它,那是值得的。您需要更改您的应用,因为机制不同。

在额外的干净列上建立索引

另一种解决方案是索引没有前导或尾随空格的列。但是您需要创建一个额外的列;在大量 table 上,此操作可能需要一些时间。好消息是一旦创建就没有更多的延迟。例如:

alter table t add column trimmed_id varchar(100) 
  generated always as (trim(identifier));

注意:您可能需要disable/enable 对该子句前后的table 进行完整性检查。 DB2 对此很挑剔。阅读手册以确保其有效。创建此专栏需要一些时间。

然后,你需要索引它:

create index ix1 on t (trimmed_id);

索引的创建也需要一些时间,但应该比上面的步骤快。

现在,一切准备就绪。您可以使用新列而不是原始列(仍然存在)来查询 table,但是这一次,您可以忘记前导和尾随空格。例如:

SELECT * FROM T WHERE trimmed_id LIKE 'ID1%ID2';

唯一的通配符现在出现在中间。此查询将比读取整个 table 快得多。事实上,字符串ID1越长,查询越快,因为选择性会更好。

现在,如果 ID2ID1 长,那么您可以反转索引以使其更快。

出于比较目的,Db2 中会忽略尾随空格,因此您只需要考虑前导空格和嵌入空格。

假设 Identifier 上有一个索引,您唯一的选择(如果您不能更改数据,或添加功能索引或索引生成的列),可能是这样的

SELECT * FROM T
WHERE
    Identifier = 'ID1 ID2'
OR  Identifier = ' ID1 ID2'
OR  Identifier = '  ID1 ID2'
OR  Identifier = 'ID1  ID2'
OR  Identifier = ' ID1  ID2'
OR  Identifier = '  ID1  ID2'

Db2 优化可能实现为 6 个索引查找,这比完整索引或 table 扫描

更快

你也可以试试这个

SELECT * FROM T
WHERE
    Identifier LIKE 'ID1 %ID2'
OR  Identifier LIKE ' ID1 %ID2'
OR  Identifier LIKE '  ID1 %ID2'

Db2 优化可能实施为 3 个索引范围扫描,

如果需要,在这两个示例中添加更多行以覆盖数据中的最大前导空格数。在第一个示例中,如果需要,也可以为嵌入空间添加更多行

表达式 REGEXP_REPLACE(TRIM(Identifier), '\s{2,}', ' ') 上的索引以及以下查询应使 Db2 使用此索引:

SELECT * 
FROM T 
WHERE REGEXP_REPLACE(TRIM(Identifier), '\s{2,}', ' ') = 'ID1 ID2'