【问题标题】:Using MATCH with OR returns results that satisfy none of the conditions将 MATCH 与 OR 一起使用会返回不满足任何条件的结果
【发布时间】:2019-09-10 07:24:08
【问题描述】:

使用 MySQL 5.6,此请求

SELECT foo
FROM bar
WHERE groupId = '1'
  AND MATCH (foo) AGAINST ('"myQuery"' IN BOOLEAN MODE);

SELECT foo
FROM bar
WHERE groupId = '1'
  AND foo like '%myQuery%';

返回两个正确的结果,但是当我将两者结合起来时:

SELECT foo
FROM bar
WHERE groupId = '1'
  AND (
    MATCH (foo) AGAINST ('"myQuery"' IN BOOLEAN MODE)
    OR foo LIKE '%myQuery%'
  );

我得到了一些额外的结果,它们没有出现在前两个请求中,并且根本不包含myQuery

我错过了括号有什么技巧吗?
或者它可能与任何类型的索引缓存有关?有时结果是正确的,但突然之间就没有了。

我也试过

WHERE (
  groupId = '1' AND MATCH (foo) AGAINST ('"myQuery"' IN BOOLEAN MODE)
) OR (
  groupId = '1' AND foo like '%myQuery%'
);

编辑:这是我请求的结果,myQuery = 'gold'

第一个和第二个返回:

'花式黄金'
'漂亮的黄金'
'超级漂亮的黄金'
'丑陋的黄金'

最后一个返回:

'飞檐钻石'
'自定义'
'花式黄金'
'漂亮的黄金'
'超级漂亮的黄金'
'丑陋的黄金'

我注意到另一件事,我运行了Optimize table bar,然后结果是正确的。我再次运行第一个请求,然后第三个结果不再正确。所以我真的怀疑与全文索引有关的东西。

编辑 2:这是一个 dbFiddle:https://www.db-fiddle.com/f/iSXdTK7EzfoQ46RgDX7wF3/1

【问题讨论】:

  • dbfiddle.uk/?rdbms=mysql_8.0 在此处创建 db 表并使用数据向我们展示您的问题
  • @tcadidot0 OR 在 mySql 中可以完美运行,不能正常使用的工具不要怪罪,也请不要试图说服人们使用其他没有的工具完全一样
  • @tcadidot0 好吧,在近 30 年的编码生涯中,我从未见过一种行为本身就很奇怪的语言功能。它要么是有缺陷的功能(你可以知道),要么是你不知道或不理解的规则,但很可能是你的代码或数据很奇怪。如果你在 mySql 中用OR 得到奇怪的结果,为什么不问像 OP 这样的问题呢?对于这里的问题,欢迎提供数据和意外结果的示例
  • 首先,我会检查您是否提交了所有数据(全文索引适用于提交的数据,尽管我不确定如何重现您所描述的内容)。你能用你运行的确切代码创建一个小提琴(见第一条评论,或 sqlfiddle.com)吗? (创建表、插入/更新、查询、优化、查询)。即使它没有显示效果,我们也可以复制并粘贴它并在本地安装上进行测试(指定您的确切版本)。如果您在 2 个独立安装(aws、本地)上发生这种情况,您/我们似乎遗漏了一个微妙之处,我们可能会在您的具体代码中发现这一点。
  • 我刚刚添加了一个 dbFiddle,以便您可以测试其行为

标签: mysql sql full-text-search mysql-5.6


【解决方案1】:

自联接似乎通过诱使数据库认为它不是两个谓词中的同名列来解决问题:

SELECT 
   t1.name 
FROM
    m as t1
    INNER JOIN m as t2 ON t1.id = t2.id 
WHERE
    t1.sId = 'N'
    AND (
      MATCH (t1.`name`) AGAINST ('"foo"' IN BOOLEAN MODE)
      OR t2.`name` LIKE '%foo%'
    );

正如我在上面的评论中提到的,在原始 SQL 中,似乎如果 MATCH 为任何行提供 TRUE 结果,则 LIKE 将匹配任何内容。通过将两个名称列视为不同,即使它们来自同一个表,这也可以解决该问题。

编辑: 有趣的是,相关的子选择并没有同样的有益效果:

SELECT 
   t1.name 
FROM
    m as t1
WHERE
    t1.sId = 'N'
    AND (
      MATCH (t1.`name`) AGAINST ('"foo"' IN BOOLEAN MODE)
      OR 1 = (SELECT 1 
              FROM m as t2 
              WHERE t1.id = t2.id 
              AND t2.`name` LIKE '%foo%' ) 
    );

【讨论】:

    【解决方案2】:

    我有一种非常强烈的感觉,即您所描述的是您希望查询看起来的样子,但您实际上是在使用代码来编写查询,并且您的代码中有一个错误,导致 myQuery null... 那么您将匹配始终匹配的'%%'

    当您调用错误时,您应该发布实际的查询,而不是对在转录以实际符合目的时查询的样子进行一些一厢情愿的解释。

    您是否使用了?? 占位符,而在数据绑定时忘记设置第二个值等于第一个值?

    【讨论】:

      【解决方案3】:

      这似乎给出了你想要的结果

      SELECT 
         name
      FROM
          m
      WHERE
          sId = 'N'
          AND MATCH (`name`) AGAINST ('"foo"' IN BOOLEAN MODE)
          OR `name` LIKE '%foo%';
      

      【讨论】:

      • sid 是从哪里来的?
      • 我使用了dbFiddle链接中的查询,应该是sId = groupId
      • 这并不要求匹配 LIKE 的行是 sId = 'N'。 AND 优先于 OR
      猜你喜欢
      • 2019-04-25
      • 1970-01-01
      • 2023-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-10
      • 2014-11-25
      • 1970-01-01
      相关资源
      最近更新 更多