SELECT如果string包含列值

问题描述 投票:0回答:3
Manufacturer
==========================
id            name      
--------------------------
1             Company Inc.
2             Google Test.
3             3M (UNITY) USA. INC.
4             CE EE

说,我有一个字符串'谷歌测试。 1257 SCS RANDOM 31233DD'并且我想查找表manufacturer中的所有行,其中ht name是给定字符串的一部分:

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)。所有值均为上限,并使用普通空格。全部以字符或数字开头,以数字,字符或特殊字符结尾。例如:'CLEVLAND DRILL(GREEN)'。值中有特殊字符,例如,.()&/

我不是在寻找效率,只要它不需要超过50毫秒来进行一次查询。

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

sql regex postgresql pattern-matching
3个回答
2
投票

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

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

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


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) || '( |$)')

How? Why?

\m .. beginning of a word. Works,因为:值以字符或数字开头 ( |$) ..一个空格或字符串的结尾。我们需要这个值,因为值:以数字,字符或特殊字符结尾

manufacturer.name的内容是该模式的核心。你想要它的所有字符的字面含义,所以通过适当地转义去除任何特殊含义。 LIKE(少数特殊字符)以及正则表达式匹配运算符~(更多特殊字符)都是如此。经常被忽视而且非常陷入困境。这让你(和界限的棘手定义)。读这个!

然后使用函数f_regexp_escape()进行演示。像name一样:

3M (UNITY) USA. INC.

变为:

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

可能很方便存储表manufacturer中容易逃脱的模式,也许作为附加列。也许添加这样的填充:

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

或者如同演示一样动态生成模式。

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

Postgres中有一系列其他模式匹配工具:

如果您的表很大,请考虑使用优化索引和短语搜索功能的full text search基础架构:


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

Demo on 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') || '( |$)');

Updated demo

© www.soinside.com 2019 - 2024. All rights reserved.