在SQL中按不同顺序查找名称和姓氏

在SQL中按不同顺序查找名称和姓氏,sql,oracle,Sql,Oracle,我正在尝试编写一个SQL查询,它能够找到以不同方式公开的相同值。 现在我试着更好地解释 我有一个包含姓名和姓氏的专栏,如果你有多个姓氏,所有的都是这样: -------------------------------------- | TABLE_1 | -------------------------------------- | NAME | -----------------

我正在尝试编写一个SQL查询,它能够找到以不同方式公开的相同值。 现在我试着更好地解释

我有一个包含姓名和姓氏的专栏,如果你有多个姓氏,所有的都是这样:

--------------------------------------
|              TABLE_1               |
--------------------------------------
|                NAME                |
--------------------------------------
|           John Frusciante          |
--------------------------------------                   
|            Gilmour David           |
--------------------------------------
|            Sinatra Frank           |
--------------------------------------
|             David Bowie            |
--------------------------------------
|           Frusciante John          |
--------------------------------------
|     Wilhelm Friedrich Nietzsche    |
--------------------------------------
问题是名字和姓氏并不总是井然有序

我怎样才能提出这样的问题

SELECT * FROM TABLE_1 WHERE NAME='JOHN FRUSCIANTE'

和FindId 2结果?

更新的解决方案-请尝试以下方法:

with patterns as -- split each word in input search_string as a match-able pattern
(
    select regexp_substr(:search_string, '\w+', 1, level) pattern -- '\w+' matches one or more word characters
    from dual
    connect by regexp_substr(:search_string, '\w+', 1, level) is not null
) ,
table_1 as 
(
    select 'John Frusciante' as name from dual union all
    select 'John Frusciante John' as name from dual union all
    select 'Gilmour David' from dual union all 
    select 'Sinatra Frank' from dual union all 
    select 'David Bowie' from dual union all 
    select 'Frusciante John' from dual union all 
    select 'Wilhelm Friedrich Nietzsche' from dual union all  
    select 'John Smith'  from dual union all
    select 'Sarah Frusciante'  from dual
)
select name from (
select t.name,
       case sum( -- sum of all matches
                case instr(upper(t.name), upper(p.pattern)) -- match occurrence of each pattern in name
                    when 0 then 0   -- 0 when pattern is not found in name
                    else 1          -- 1 when each pattern is found in name
                end
                )
        when regexp_count(:search_string, '\w+', 1) -- count number of words in search string
        then 'matched'      -- match found for each word in search string
        else 'not matched'  -- not all words in search string is matched in the name
    end match_result
from table_1 t, patterns p
group by t.name
) where match_result = 'matched'
;

对于search_string=john frusciante,输出如下:

NAME
---------------
John Frusciante
John John Frusciante
Frusciante John
select * from table_1 where regexp_like(name, '^(JOHN|FRUSCIANTE)', 'i');
我最初的方法是在一个字符串中匹配多个模式,如下所示:

NAME
---------------
John Frusciante
John John Frusciante
Frusciante John
select * from table_1 where regexp_like(name, '^(JOHN|FRUSCIANTE)', 'i');
然而,正如@mathguy正确指出的那样,这也将返回“约翰·贝克特”和“莎拉·弗鲁西安特”
这里有一种方法——忽略我在对你的问题的评论中提到的大部分微妙之处。我唯一提到的是不区分大小写的搜索

输入,如“John Frusciante”,作为绑定变量:i\u name提供。名字可以是一个、两个、三个或任何其他数量的代币——它们可以以任何顺序出现,包括像侯赛因·奥巴马这样的无意义的顺序,其中奥巴马是姓,巴拉克·侯赛因是名;美国术语中的名字和中间名。在测试中,我使用“John Frusciante”作为绑定变量

正则表达式很方便,但不快。使用标准字符串函数可以通过各种方式加快查询速度,但在Oracle 12.1或更高版本中也可以通过使用横向或交叉应用子句等方式加快查询速度。如果Oracle数据库版本为11.1或更低版本,则会出现listagg问题,因为此函数仅在11.2中引入

该策略很简单——将每个名称分解为其标记,然后按字母顺序再次聚合它们。我假设该表有一个id列(如果没有),如果数据在存储的表中,我可以使用rowid,或者在另一个步骤中动态创建一个id

with
  table_1 (id, name) as (
    select 1, 'John Frusciante'             from dual union all
    select 2, 'Gilmour David'               from dual union all  
    select 3, 'Sinatra Frank'               from dual union all
    select 4, 'David Bowie'                 from dual union all
    select 5, 'Frusciante John'             from dual union all
    select 6, 'Wilhelm Friedrich Nietzsche' from dual
  )
, prep (id, name, ordered_name) as (
    select  id, name, 
            listagg(regexp_substr(name,'\S+', 1, level), ' ') 
              within group
                (order by regexp_substr(name,'\S+', 1, level))
    from    table_1
    connect by  level <= regexp_count(name, '\S+')
            and prior id = id
            and prior sys_guid() is not null
    group   by id, name
  )
select name
from   prep
where  lower(ordered_name) = 
         (select  lower(listagg(regexp_substr(:i_name,'\S+', 1, level), ' ') 
                  within group 
                    (order by regexp_substr(:i_name,'\S+', 1, level)))
          from    dual
          connect by level <= regexp_count(:i_name, '\S+')
         )
;

您可以使用下面的工具来实现您的目的

1这是基于给定的搜索字符串,该字符串的名字和姓氏应按任意顺序排列

2此外,名字和姓氏不应相同

        WITH table_1 (id, name)
             AS (SELECT 1,
                        'John george Frusciante'
                 FROM   dual
                 UNION ALL
                 SELECT 2,
                        'Gilmour David'
                 FROM   dual
                 UNION ALL
                 SELECT 3,
                        'Sinatra Frank'
                 FROM   dual
                 UNION ALL
                 SELECT 4,
                        'JOHN Frusciante'
                 FROM   dual
                 UNION ALL
                 SELECT 5,
                        'Friedrich Nietzsche Wilhelm'
                 FROM   dual
                 UNION ALL
                 SELECT 6,
                        'Wilhelm Friedrich Nietzsche'
                 FROM   dual),
                 input1 as(select replace('Wilhelm Friedrich Nietzsche',' ','|') string1 from dual)
        SELECT a.*
        FROM   table_1 a,input1
        WHERE regexp_like(name, '^'||string1, 'i')
        AND regexp_like(name, string1||'$', 'i')
        AND upper(REGEXP_substr(name,'^(\S*)'))<>upper(REGEXP_substr(name,'(\S*)$'));

像这样简单的事情对你来说可能很有用:

SELECT *
FROM TABLE_1
WHERE NAME LIKE '%JOHN%'
AND NAME LIKE '%FRUSCIANTE%'

这不完全是小事,但可以做到。不过有几个问题。1您的Oracle数据库版本是什么?例如,11.2.0.3-如果您不知道,请从v$version运行select banner,查看它的说明。2你关心约翰对约翰的案子吗?约翰·多伊vs.J·多伊vs.J·多伊怎么样?4个连字符的名字?就像让-保罗·萨特一样,这和让-保罗·萨特一样,也和保罗·萨特一样吗?5不重要,但为了使事情更简单-此表中是否还有主键列?或者所有的名字都是不同的吗?1 10.2.0.1.0这是版本2当然,我有一个巨大的表格,其中包含各种各样的名字3个名字可能看起来也像这样4这个例子意大利人的名字并不常见,但如果我的代码能够检测到它,ir会更好。5有一个名为“NUM”的列是主要的关键字,10.2.0.1是坏消息。我的解决方案使用Listag,它仅在Oracle 11.2之后才可用,而Oracle 11.2本身几乎没有扩展支持。还有其他方法可以聚合字符串,但解决方案将比我发布的内容更加复杂,你说我发布的内容已经太多了…-这将使它无法维持。我不认为这是OP想要的。您的查询还将返回John Becket和Sarah Frusciante。此外,他可能正在寻找一种通用解决方案,其中搜索的名称是一个输入,可能是一个绑定变量,因此其组件名称不能在查询中硬编码。@mathguy:没错,感谢您的审阅和指出。根据你的建议,我编辑了一个稍微不同的方法。你的解决方案也非常有效,并且在原始帖子评论上提出了非常好的问题!Cheers@ManneredPizza-第一部分只是模拟您的数据。删除-查询应以prep id开始,…-在prep中使用实际的表名和列名。问题是我正在搜索的名称是一个输入变量,我不知道它是否由两个或更多部分组成