忽略 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
越长,查询越快,因为选择性会更好。
现在,如果 ID2
比 ID1
长,那么您可以反转索引以使其更快。
出于比较目的,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'
我 运行 在一个大型 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
越长,查询越快,因为选择性会更好。
现在,如果 ID2
比 ID1
长,那么您可以反转索引以使其更快。
出于比较目的,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'