【问题标题】:Exact match, or do partial match if no exact match, in a single query?完全匹配,或者如果没有完全匹配,则在单个查询中进行部分匹配?
【发布时间】:2014-04-28 15:53:30
【问题描述】:

我需要在以下条件下搜索 MySQL 数据库:

1) 如果找到关键字完全匹配,则返回行数和完全匹配的结果。

2) 如果没有找到完全匹配,则返回部分匹配的行数和结果。

例如,如果关键字是 cake,并且表中有以下行,则搜索应该只返回第 1 行和第 5 行。

1| cake
2| pancake
3| cupcake
4| fruitcake
5| cake
6| pie

但是,如果仅存在以下行,则搜索应仅返回第 2-4 行。

2| pancake
3| cupcake
4| fruitcake
6| pie

在两个查询中这样做很简单,例如:

SELECT SQL_CALC_FOUND_ROWS fields FROM table WHERE word = `cake`

//PHP checks for number of rows, if 0, do second query

SELECT SQL_CALC_FOUND_ROWS fields FROM table WHERE word LIKE '%cake%'

问题:为了减少访问数据库的次数,有没有办法在单个查询中做到这一点?

我能想出的最佳解决方案是通过 OR 进行匹配,使用 ORDER BY 来支持精确匹配。但是,如果找到完全匹配,则要切断部分匹配需要遍历结果,如果完全匹配返回 2,000 个结果并且部分匹配返回 5,000 个以上,这是不可行的,但我只想显示前 25 个,同时仍然显示2,000 个计数。

【问题讨论】:

    标签: mysql


    【解决方案1】:

    编辑 3:

    following 确实有效:

    SELECT 
        COALESCE(NULLIF(COUNT(exact.Id), 0), COUNT(partial.Id))
    FROM
        Test AS partial
    LEFT JOIN
        Test AS exact ON exact.Id = partial.Id AND exact.Name = 'pi'
    WHERE
        partial.Name LIKE '%pi%'
    

    我在这里删除了我最后的文字,因为它是错误的。

    编辑 4:

    以下是用 TSQL 编写的(我不知道正确的 MySql 语法)并返回实际的行(您可以将其包装在 MySql 服务器上的 StoredProcedure 中):

    CREATE TABLE #Result (Id int, Name nvarchar(max));
    
    INSERT INTO #Result
    SELECT * FROM #YourCakeTable WHERE Name = 'cake'
    
    if(FOUND_ROWS() = 0)
        INSERT INTO #Result
        SELECT * FROM #YourCakeTable WHERE Name like '%cake%'
    
    SELECT * FROM #Result
    

    【讨论】:

    • 也许我做错了什么,但它没有按预期工作。我修改了你的小提琴以匹配我在问题中的愚蠢蛋糕场景。这会正确返回 1 和 5:sqlfiddle.com/#!2/ac5f1/1 但这不会返回任何内容(应该返回 2-4)sqlfiddle.com/#!2/5cf40/1
    • 刚刚意识到示例使用= 而不是LIKE 进行部分匹配。通过该更正,第二个小提琴正确返回 2-4 (sqlfiddle.com/#!2/5cf40/2),但是第一个小提琴也返回 2-4,而不是 1 和 5 (sqlfiddle.com/#!2/ac5f1/2)
    • 感谢您的更新,感谢您的努力。我越来越近了,计数是正确的,但我真正想要的是结果中带有 id 和 name 字段的实际行 2-4 或 1 和 5,希望我也可以将 SQL_CALC_FOUND_ROWS 放在开头以获得来自同一查询的 3 或 2 个 cnt。我正在玩这个,看看我是否可以让它做到这一点,但是我意识到如果没有一个如此复杂的查询,它可能是不可能的,只做两个查询会更快..
    • 我不太了解 MySql(我通常使用 TSQL),但也许您可以使用可以在 MySql 数据库中设计的函数之类的东西。在那里你可以创建一个临时表。我认为查询将更具可读性并且更易于维护。
    【解决方案2】:

    我不确定 MYSQL sintax,但你可以这样做:

    SELECT (CASE WHEN Exact.Apparence IS NULL THEN Partial.Apparence ELSE Exact.Apparence END) as Apparence FROM 
    (SELECT COUNT(word) as Apparence FROM Table WHERE word='cake') as Exact INNER JOIN 
    (SELECT COUNT(word) as Apparence FROM Table WHERE word like '%cake%') as Partial ON 1=1
    

    如果对你有帮助,请告诉我

    【讨论】:

    • 谢谢。我会等待其他答案,但似乎连接和案例可能是探索的最佳途径......可能会使我未简化的场景比我想要的更混乱,但这是生活权衡的一部分:)
    【解决方案3】:
    SELECT word, count(*) as ExactMatches, 
    if(count = 0, (SELECT count(*) from table WHERE word LIKE '%word%' order by word), NULL) as PartialMatches
    FROM  table 
    order by word
    

    我认为在这里你会得到你的

    word|count exacts|count partials|
    
    cake|100| null
    cake|0| 50
    

    【讨论】:

    • 我不确定这是否是您所需要的全部,但我认为它会起作用,不幸的是它受到 order by 子句的限制......
    【解决方案4】:
    SELECT (CASE WHEN Exact.Apparence IS NULL THEN Partial.Apparence ELSE Exact.Apparence END) as Apparence FROM 
    (SELECT COUNT(word) as Apparence FROM Table WHERE word='cake') as Exact INNER JOIN 
    (SELECT COUNT(word) as Apparence FROM Table WHERE word like '%cake%') as Partial ON 1=1
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-01
      • 2021-04-06
      • 2012-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多