【问题标题】:oracle sql search by preferenceoracle sql 按偏好搜索
【发布时间】:2013-11-29 23:04:33
【问题描述】:

拿一张样品表:

WITH t(val) AS
     ( SELECT 'my Name' FROM dual
     UNION
     SELECT 'my name' FROM dual
     UNION
     SELECT 'my naim' FROM dual
     UNION
     SELECT 'MY kat' FROM dual
     UNION
     select 'my katt' from dual
    )
SELECT * FROM t;

我需要一个偏好的输出:

查询应该首先在表中搜索完全匹配, 如果没有找到则搜索lowering the case,如果没有找到则只搜索soundex,。所以最终输出类似于:

WHERE val = 'my Name'
  OR lower(val) = lower('my Name')
  OR soundex(lower(val)) = soundex(lower('my Name'))

应该是:

output
-----
my Name

提前致谢。

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    按照您的描述进行过滤,然后按该顺序排序,然后获取第一条记录:

    WITH t AS
         ( SELECT 'my Name' as val FROM dual
         UNION
         SELECT 'my name' FROM dual
         UNION
         SELECT 'my naim' FROM dual
         UNION
         SELECT 'MY kat' FROM dual
         UNION
         select 'my katt' from dual
        )
    SELECT * FROM
    (
      SELECT * FROM t
      WHERE val = 'my Name'
      OR lower(val) = lower('my Name')
      OR soundex(lower(val)) = soundex(lower('my Name'))
      order by
        case 
          when val = 'my Name' then 1
          when lower(val) = lower('my Name') then 2
          when soundex(lower(val)) = soundex(lower('my Name')) then 3
        end
    )
    WHERE ROWNUM = 1;
    

    【讨论】:

    • 对不起,我不明白。您正在寻找一个名字的最佳匹配。因此,您要么获得一条记录,要么没有。这就是 rownum = 1 的作用。您还有其他要求吗?
    • 1) 内部查询的顺序不生效,2) ROWNUM 如前所述。所以我修改了你的一些逻辑,自己看我的回答。
    • order by 对内部查询进行排序。 rownum = 1 因此采用内部查询的排序记录的第一条记录。我看不出为什么这不起作用。它适用于我的 dbms (Ora 10g)。它对你不起作用吗?执行查询得到什么结果?
    • 好的。我会再检查一次:)
    • 是的,where 子句在 order by 之前执行。但是正如您在上面的 select 语句中清楚地看到的那样,内部 select 有一个 order by,但没有 where 子句,而外部 select 有一个 where 子句,但没有 order by。因此,首先执行的顺序或位置的问题在这里不适用。内部选择在外部选择之前执行。所以外层得到内层已经排序的结果集的第一条记录。
    【解决方案2】:

    对于大型数据集,如果之前的任何测试找到匹配项,您可能希望避免不必要的测试。

    with t as
       ( SELECT 'my Name' as val FROM dual
       UNION
       SELECT 'my name' FROM dual
       UNION
       SELECT 'my naim' FROM dual
       UNION
       SELECT 'MY kat' FROM dual
       UNION
       select 'my katt' from dual
        )
      exact_match as (
        select *
        from   t
        where  val = 'my Name'),
      lower_case_match as (
        select *
        from   t
        where  lower(val) = lower('my Name') and
               not exists (select null from exact_match)),
      soundex_match as (
        select *
        from   t
        where  soundex(val) = soundex('my Name') and
               not exists (select null from lower_case_match) and
               not exists (select null from exact_match))
    select * from exact_match
    union all
    select * from lower_case_match
    union all
    select * from soundex_match;
    

    Oracle 很可能会具体化前两个搜索公用表表达式的结果集,以使后续表达式更有效地测试它们是否返回结果。如果第一个“exact_match”搜索返回结果,则不需要执行后续搜索。

    【讨论】:

    • 获知。 :) 谢谢。让我研究一下解释计划。和+1。请使用 KEEP FIRST dense_rank 查看我的答案
    【解决方案3】:

    可能你需要oracle文本,更准确地说是查询松弛:http://docs.oracle.com/cd/B28359_01/text.111/b28303/query.htm#i1007593

    你可以模仿类似的东西:

    select
     *
    from
     (select *,
       case
          when condition1 then 3
          when condition2 then 2
          when condition3 then 1
       end relevance
      from
        table
      where
        (condition1 or condition2 or condition3)
      )
    order by
      relevance desc
    

    【讨论】:

    • 感谢您的链接,您能否更准确地说我应该对给定的样本使用多精确?
    • Oracle 文本是一个比较大而且不是那么容易的话题。我无法抽象出几行。无论如何,如果您认为这太过分了,那么您可能可以使用第一个条件运行查询,如果它没有找到任何东西,那么使用第二个条件等等。不是很好的解决方案,但更简单,并且(开发人员)更省时。
    • 这就是我的查询。
    • 我找到了一个准替代方案。我修改了答案。
    • 由于上述数据仅为示例,如果精确输入不在表中,您的查询逻辑将不起作用。如果= 不起作用,我需要获取查询小写的输出,所以我使用了排名,请参阅我的答案。
    【解决方案4】:

    我的怎么样:(感谢@Thorsten Kettner)。

    SELECT val FROM(
      SELECT val, DENSE_RANK()OVER(ORDER BY CASE WHEN val = 'my Name' THEN 1
                                                 WHEN lower(val) = lower('my Name') THEN 2
                                                 WHEN soundex(lower(val)) = soundex(lower('my Name')) THEN 3
                                            END
                                   )rnk
      FROM t
      WHERE val = 'my Name'
      OR lower(val) = lower('my Name')
      OR soundex(lower(val)) = soundex(lower('my Name'))
    )
    WHERE rnk  = 1; 
    

    另一个 FIRST ,由 Oracle 专门针对我们的目的:

    SELECT MAX(val) keep (dense_rank FIRST ORDER BY priority) AS val
    FROM
         (SELECT t.* ,
              CASE WHEN val = 'my Name' THEN 1
                   WHEN lower(val) = lower('my Name') THEN 2
                   WHEN soundex(lower(val)) = soundex(lower('my Name')) THEN 3
              END priority
         FROM t
         WHERE val                = 'my Name'
           OR lower(val)          = lower('my Name')
           OR soundex(lower(val)) = soundex(lower('my Name'))
         );
    

    【讨论】:

    • 是的,您可以为此使用分析等级函数。请注意:如果名称没有匹配,但有两个 soundex 匹配(例如“mi naim”和“my naim”),该怎么办。我的解决方案会给你这些匹配之一,你的返回两个。由您决定您喜欢什么。
    猜你喜欢
    • 2013-04-15
    • 1970-01-01
    • 1970-01-01
    • 2015-05-31
    • 2014-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-13
    相关资源
    最近更新 更多