【问题标题】:Weird behavior using MATCH () AGAINST () with PDO (syntax error)使用带有 PDO 的 MATCH () AGAINST () 的奇怪行为(语法错误)
【发布时间】:2016-02-24 15:25:11
【问题描述】:

我正在处理一个在 FULLTEXT 索引上使用 MATCH () AGAINST () 的查询。

奇怪的是,该查询在 PHP 的 PDO 中不起作用,但 适用于 HeidiSQL 甚至 MySQL 控制台(mysql -u [...])

这是我正在处理的查询:

SELECT *
FROM `announcements`
WHERE MATCH (`name`, `email`, `trademark`, `model`, `phone`, `city`) AGAINST ('+Ravenna*' IN BOOLEAN MODE)

这是 PDO 的错误:

SQLSTATE[42000]:语法错误或访问冲突:1064 您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以了解在附近使用的正确语法

'?) against ('?' in boolean mode))' 在第 1 行

SELECT count(*) AS AGGREGATE
FROM `announcements`
WHERE `archived_at` IS NULL
  AND (MATCH (`name`, `email`,  `trademark`, `model`, `phone`, `city`) against ('+Ravenna*' IN boolean MODE))

编辑:

这是导致错误的函数:

[...]

$this->customQueries[] = function ($query) use ($words) {

    $columnsToSearch = array_map(function ($key) {
        return "`{$key}`";
    }, [
        'name', 'email', 'trademark', 'model', 'phone', 'city'
    ]);

    $matchColumns = implode(', ', $columnsToSearch);
    $againstWords = value(function () use ($words) {
        $string = "";

        foreach ($words as $word) {
            $string .= "+{$word}* ";
        }

        $string = trim($string);

        return $string;
    });

    // This is specifically the function that causes the error.
    $query->whereNested(function ($q) use ($matchColumns, $againstWords) {
        $q->whereRaw("match (?) against ('?' in boolean mode)", [$matchColumns, $againstWords]);
    });
};

我不明白为什么它只在 PDO 上不起作用。

我已经在 StackOverflow 上搜索过了,所有的单引号都已经检查过了。

【问题讨论】:

  • 能否添加相关的PDO代码?
  • 我不直接使用PDO,它是Illuminate抽象出来的。 @JoachimIsaksson 添加了一些代码。
  • 去掉反引号有什么不同吗? (仅当您的列名是保留字时才真正需要它们)。无论如何,我认为照亮是这里的罪魁祸首。他们显然把它变成了一个准备好的语句并产生了无效的语法。
  • 我也试过不加反引号,结果一样。
  • 如果这是 PDO,那么匹配 ? 将获得单个字符串,而不是您的工作示例所建议的列列表。您的against 部分也有类似的情况,因为您将? 放在引号内,这也可能部分失败。

标签: php mysql pdo full-text-search lumen


【解决方案1】:

您不能将逗号分隔的列名字符串作为参数插入到准备好的语句中。相反,您应该将它们注入您的 SQL 语句中,而不是将它们作为参数传递。

其次,永远不要用引号将占位符 (?) 括起来。

所以尝试修改你的代码:

$query->whereNested(function ($q) use ($matchColumns, $againstWords) {
    $q->whereRaw("match ($matchColumns) against (? in boolean mode)", [$againstWords]);
 });

您可能认为在 SQL 字符串中注入 $matchColumns 这样不好,但由于您可以完全控制该值,因此没有风险(SQL 注入)。

【讨论】:

  • 这似乎解决了这个问题,所以我感谢你,因为我已经为此苦苦挣扎了整整一个小时(或更长时间)!但是我想知道为什么我不能使用逗号分隔的字符串作为准备好的语句的参数,你能启发我吗?
  • 准备好的语句由 SQL 引擎编译。这只有在 SQL 在表和列方面是完整的情况下才有可能。在语句的编译过程中,只能留下文字值未知。例如,请参阅 PHP 文档中的此评论:us3.php.net/manual/en/book.pdo.php#69304
  • 谢谢你,你救了我的命。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-14
  • 1970-01-01
  • 2015-07-05
相关资源
最近更新 更多