【问题标题】:Use of LIKE in Oracle with high number of variables using varray在 Oracle 中使用 LIKE 并使用 varray 进行大量变量
【发布时间】:2019-11-27 07:43:38
【问题描述】:

我有一个名为institutions 的表,其中包含机构的名称。每次出现新名称时,函数都需要找到与现有机构相关的任何匹配项。例如,我们有以下机构表

NAME
FORMULA VIEW UNIVERSITY RESEARCH
FOURMULA VULCAN COLLEGE INSTITUTE
MOUNT VU FOOD GROWERS
FORMULA VU CAFE SHOP

以下同义词表

WORD            SYN_LIST
EDUCATION       SCHOOL, UNIVERSITY, COLLEGE, TRAINING
RESTAURANT      BAR, BISTRO, BREAKFAST, BUFFET, CABARET, CAFE, FOOD, GALLEY, GASTROPUB, GRILL
STORE           CONVENIENCE, FOOD, GROCER, GROCERY, MARKET, MART, SHOP, STORE, VARIETY
REFRIGERATION   APPLIANCE, COLDSTORAGE, FOODLOCKER, FREEZE, FRIDGE, ICE, REFRIGERATION

最后是替换表这个词:

WORD        SUBS_LIST
MOUNTAIN    MOUNTAIN, MOUNT, MT, MTN
VIEW        VIEW, VU
FORMULA     FORMULA, 4MULA, FOURMULA

最重要的是,来自机构的名称被归类为name|field。要完成将单词分类为name|field,只需使用两个输入参数手动传递给函数。例如,名称FORMULA VIEW UNIVERSITY RESEARCH 分解为FORMULA VIEW 作为名称,UNIVERSITY RESEARCH 作为字段。在机构中搜索名称时,名称部分(在本例中为 FORMULA VIEW 必须找到完全匹配)和至少一个字段词(在本例中为 UNIVERSITY 或/和 RESEARCH)。

之前的匹配规则如下所示:

select
from institutions
where name like '%FORMULA VIEW%' || ' %UNIVERSITY% '
UNION  
select
from institutions
where name like '%FORMULA VIEW%' || ' %RESEARCH% '

然后,如果出现新名称,例如 FORMULA VIEW COLLEGE,就足以找到与 FORMULA VIEW UNIVERSITY RESEARCH 匹配的名称。

最后,同义词和单词替换适用于接收到的名称,并且每个单词可以在同义词表中的多个类别中(因为单词 food 包含在 store 和 restaurant 中),然后在查找匹配项的功能。考虑到前面的三个表,考虑一个新名称,例如MOUNTAIN VIEW STORE OFFICE,然后该函数在机构表中找到MOUNT VU FOOD GROWERSalready 作为匹配项。发生这种匹配是因为 food 有两个同义词,然后生成了两个词:MOUNTAIN VIEW FOOD GROWERSMOUNTAIN VIEW STORE GROWERS

select
from institutions
where name like '%MOUNTAIN VIEW%' || ' %STORE% '
UNION
select
from institutions
where name like '%MOUNTAIN VIEW%' || ' %OFFICE% '

即使只有 STORE 是匹配项,而不是 OFFICE,这足以识别为匹配项并指示 STORE 中的冲突。

我一直在研究这个问题并创建了this DBFiddle 以包含前面解释的大部分内容并尝试解决问题,但我无法弄清楚如何添加搜索逻辑对于 name 类别中的完全匹配并传递一个 field 类别,直到传递其余的 field 类别,如示例中所示:

select
from institutions
where name like '%MOUNTAIN VIEW%' || ' %STORE% '
UNION
select
from institutions
where name like '%MOUNTAIN VIEW%' || ' %OFFICE% '

如果您找到更适合解决此问题的其他解决方案,请告诉我。我希望我已经足够清楚了,请让我知道任何问题。

【问题讨论】:

    标签: sql oracle query-optimization sql-like


    【解决方案1】:

    不要将数据存储为分隔字符串;为每个项目使用单独的行:

    CREATE TABLE synonyms (
      id          NUMBER(20,0)
                  GENERATED ALWAYS AS IDENTITY
                  PRIMARY KEY,
      word        VARCHAR(50)
                  NOT NULL,
      replacement VARCHAR(50)
                  NOT NULL,
      UNIQUE ( replacement )
    );
    

    您可以将 PL/SQL 函数写入split a string

    然后,如果您有一组单词,您可以在查询中进行比较:

    create or replace function replace_names(
      i_sentence IN VARCHAR2
    ) return string_list
    IS
      p_words    string_list := split_string( i_sentence, ' ' );
      p_replaced string_list;
    BEGIN
      SELECT COALESCE( s.word, w.word )
      BULK COLLECT INTO p_replaced
      FROM   synonyms s
             RIGHT OUTER JOIN (
               SELECT ROWNUM AS rn,
                      COLUMN_VALUE AS word
               FROM   TABLE( p_words )
             ) w
             ON ( s.replacement = w.word )
      ORDER BY w.rn;
    
      RETURN p_replaced;
    END;
    /
    

    然后你的测试变成:

    select * from TABLE( replace_names('MOUNT VU FOOD GROWERS' ) );
    

    输出:

    | COLUMN_VALUE | | :----------- | |山 | |查看 | |食品 | |种植者 |

    SELECT name,
           column_value corrected_name
    FROM   institutions i
           CROSS JOIN TABLE(replace_names(i.name));
    

    输出:

    姓名 | CORRECTED_NAME :-------------------------------- | :------------- 公式查看大学 |公式 公式查看大学 |看法 公式查看大学 |教育 FOURMULA 火神学院 |公式 FOURMULA 火神学院 |火神 FOURMULA 火神学院 |教育 山景餐厅种植者 |山 山景餐厅种植者 |看法 山景餐厅种植者 |食物 山景餐厅种植者 |种植者 FORMULA VU 咖啡厅 |公式 FORMULA VU 咖啡厅 |看法 FORMULA VU 咖啡厅 |食物

    和:

    SELECT name,
           column_value AS corrected_name
    FROM   institutions i
           CROSS JOIN TABLE( replace_names( i.name ) )
    WHERE  column_value MEMBER OF replace_names('MOUNTAIN VU FOOD'); 
    

    输出:

    姓名 | CORRECTED_NAME :-------------------------------- | :------------- 公式查看大学 |看法 山景餐厅种植者 |山 山景餐厅种植者 |看法 山景餐厅种植者 |食物 FORMULA VU 咖啡厅 |看法 FORMULA VU 咖啡厅 |食物

    和:

    SELECT name,
           ( SELECT LISTAGG( column_value, ' ' ) WITHIN GROUP ( ORDER BY ROWNUM )
             FROM   TABLE( corrected_words )
           ) AS corrected_name,
           num_matches
    FROM   (
      SELECT name,
             replace_names( name ) AS corrected_words,
             CARDINALITY( replace_names( name ) MULTISET INTERSECT replace_names('MOUNTAIN VU FOOD') ) AS num_matches
      FROM   institutions
    )
    WHERE  num_matches > 0
    ORDER BY num_matches DESC; 
    

    输出:

    姓名 | CORRECTED_NAME | NUM_MATCHES :-------------------------------- | :------------------------- | ----------: 山景餐厅种植者 |山景粮食种植者 | 3 FORMULA VU 咖啡厅 |配方查看食物 | 2 公式查看大学 |公式视图教育 | 1

    db小提琴here

    【讨论】:

    • 感谢@MT0 的帮助,我对现在需要使用 Postgresql 的问题进行了更改,我尝试在 Postgresql 中复制您的解决方案,但我无法成功运行您的解决方案的最后两个查询。这是我尝试转换为 Postgresql 的 dbfiddle:dbfiddle.uk/…
    • @JuanPerez Ask a new question 而不是尝试更改 cmets 中现有的范围。
    【解决方案2】:

    你的问题是你的数据模型。

    1. 如果前两个词是名称,后两个词是字段,那么为什么要将它们存储在一列而不是两列?
    2. 在关系数据库中,不应存储逗号分隔的字符串。 (至少只要您想使用单独的部分。)

    那么,同义词和替代词之间没有太大区别,因为两者都归结为“不同的词意味着同一件事”。

    让我们简化数据模型。 (您不必在以下范围内执行此操作,并保留单独的同义词和替换表,尽管已更改,甚至可以连接机构字符串,如果您愿意的话):

    机构 姓名 |场地 ----------------+-------------------- 公式视图 |大学研究 FOURMULA 火神 |学院 似曾相识 |粮食种植者 公式似曾相识 |咖啡店 word_grp grp |单词 ----------------+-------------------- 教育 |学校 教育 |大学 教育 |大学 教育 |训练 ... | .. 山 |山 山 |山 山 |公吨 山 |中期票据 ... | ..

    对于字符串替换,您可以编写一个函数,通过将单词替换为它们的组词来规范化字符串:

    create or replace function normstring(in_str varchar2) return varchar2 is
      i integer := 1;
      v_str varchar2(4000) := in_str;
      v_grp varchar2(100);
      v_word varchar2(100);
    begin
      while regexp_substr(v_str, '\w+', 1, i) is not null loop
        v_word := regexp_substr(v_str, '\w+', 1, i);
        select max(grp) into v_grp from word_grp where word = v_word;
        if v_grp is not null then
          v_str := replace(v_str, v_word, v_grp);
        end if;
        i := i + 1;
      end loop;
      return v_str;
    end normstring;
    

    然后,您可以使用上述函数查找新名称和字段。如果存在名称和字段都匹配的行,则首先出现。然后是只有名称匹配的所有行。如果名称上什至没有匹配项,则不返回任何行。查询很简单:

    select *
    from institution i 
    where normstring(i.name) = normstring(:name)
    order by
      d.name, d.field, case when normstring(i.field) = normstring(:field) then 1 else 2 end;
    

    演示:https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=9d17aa5fdfd7bb04d78f4087792b3bce

    【讨论】:

      猜你喜欢
      • 2020-03-17
      • 2011-04-16
      • 1970-01-01
      • 1970-01-01
      • 2012-01-21
      • 1970-01-01
      • 2017-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多