0
Manufacturer
==========================
id            name      
--------------------------
1             Company Inc.
2             Google Test.
3             3M (UNITY) USA. INC.
4             CE EE

比如说,我有一个字符串'Google Test. 1257 SCS RANDOM 31233DD'我想在表中找到所有行,manufacturer其中 htname是给定字符串的一部分:

SELECT * FROM manufacturer
WHERE 'Google Test. 1257 SCS RANDOM 31233DD' ILIKE '%' || name || '%' 

正确返回:

id            name      
--------------------------
2             Google Test.

但是当我这样做时:

SELECT * FROM manufacturer
WHERE '3dad QTICE EEN ' ILIKE  '%' || name || '%'

它返回:

id            name      
--------------------------
4             CE EE

我不想要这样的部分匹配。单词中间name不匹配。我试过substring()

SELECT * from manufacturer
WHERE  SUBSTRING('Google Test. 1257 SCS RANDOM 31233DD' from name) != '';

但我得到:

ERROR: invalid regular expression: quantifier operand invalid

不幸的是,我没有确切的规范,因为我正在从外部数据库查询这个。但据我所见,列是varchar(256). 所有值都大写并使用纯空格。全部以字符或数字开头,以数字、字符或特殊字符结尾。例如:“克利夫兰钻头(绿色)”。值中有特殊字符,如,.()&/

只要执行一次查询的时间不超过 50 毫秒,我并不是真的在寻找效率。

截至目前,大约有 10000 多个条目,但它可能会随着时间的推移而增长。

4

3 回答 3

2

所有值都以字符或数字开头,以数字、字符或特殊字符结尾。... 值中有特殊字符,例如,.()&/.

我建议使用正则表达式匹配运算符~。仔细定义边界转义以下特殊字符name

创建一次

CREATE OR REPLACE FUNCTION f_regexp_escape(text)
  RETURNS text AS
$func$
SELECT regexp_replace($1, '([!$()*+.:<=>?[\\\]^{|}-])', '\\\1', 'g')
$func$  LANGUAGE sql IMMUTABLE;

然后:

SELECT * FROM manufacturer
WHERE  '3dad QTICE EEN ' ~ ('\m' || f_regexp_escape(name) || '( |$)')

如何?为什么?

\m..一个词的开头。有效,因为:值以字符或数字开头
( |$).. 空格或字符串结尾。我们需要这个,因为值:以数字、字符或特殊字符结尾

的内容是模式manufacturer.name的核心。您想要其所有字符的字面意义,因此通过正确转义去除任何特殊含义。这适用于(少数特殊字符)以及正则表达式匹配运算符(更多特殊字符)。经常被忽视并且是一个陷阱。这得到了你(以及边界的棘手定义)。读这个!LIKE~

然后使用f_regexp_escape()演示的功能。一个name喜欢:

3M (UNITY) USA. INC.

变成:

3M \(UNITY\) USA\. INC\.

在 table 中存储容易转义的模式可能很方便manufacturer,可能作为附加列。也许像这样添加填充:

\m3M \(UNITY\) USA\. INC\.( |$)

或者像演示的那样动态生成模式。

这种方式name可以是单个单词或整个短语,并以任何字符结尾。但是在另一边的“单词”中间,开始和结束永远不会匹配。

Postgres 中有许多其他模式匹配工具:

如果您的表很大,请考虑具有优化索引和短语搜索功能的全文搜索基础架构:

于 2019-04-10T23:51:46.780 回答
2

要解决此问题,您确实需要使用正则表达式,因为在字符串的任一侧添加空格将不会在行的开头或结尾匹配。通过使用正则表达式,我们也可以检查这种情况。例如:

SELECT *
FROM manufacturer
WHERE 'Google Test. 1257 36700 SCS RANDOM WORD 31233DD' ~ ('(^| )' || name || '( |$)');

输出:

id  name
2   Google Test.

询问:

SELECT *
FROM manufacturer
WHERE '3dad QTICE EEN ' ~ ('(^| )' || name || '( |$)');

输出:

There are no results to be displayed.

询问:

SELECT *
FROM manufacturer
WHERE 'CE EE ' ~ ('(^| )' || name || '( |$)');

输出:

id  name
4   CE EE

dbfiddle 上的演示

更新

因为name表中的值可以包含在正则表达式中具有特殊含义的字符,所以在将名称包含到正则表达式之前需要对它们进行转义。您可以使用REGEXP_REPLACE例如

REGEXP_REPLACE(name, '([\\.+*?[^\]$(){}=!<>|:\-#])', '\\\1', 'g')

所以你的查询应该是:

SELECT *
FROM manufacturer
WHERE 'Google Test. 1257 36700 SCS RANDOM WORD 31233DD' ~ ('(^| )' || REGEXP_REPLACE(name, '([\\.+*?[^\]$(){}=!<>|:\-#])', '\\\1', 'g') || '( |$)');

更新的演示

于 2019-04-10T23:27:15.447 回答
2

一种方法LIKE是在开头和结尾添加空格:

SELECT *
FROM db
WHERE ' ' || '3dad QTICE EEN ' || ' ' ILIKE  '% ' || manufacturer || ' %'

如果您需要更复杂的匹配,那么您可能需要使用带有单词边界的正则表达式。

于 2019-04-10T22:40:53.047 回答