【问题标题】:PDO - SQLSTATE[HY093]: Invalid parameter number with UNION queryPDO - SQLSTATE[HY093]:UNION 查询的参数号无效
【发布时间】:2014-02-10 09:34:38
【问题描述】:

我整个晚上都在研究并试图找出我的搜索查询出了什么问题。我使用联合查询和分页进行一些通配符搜索。

$current_page = 0;

$search1 = $search;
$search2 = $search."%";
$search3 = "%".$search."%";

$pdo = DB::connection()->getPdo();

$stmt = $pdo->prepare('
    SELECT id, desc FROM table WHERE desc LIKE :search1 LIMIT :skip, 15
    UNION
    SELECT id, desc FROM table WHERE desc LIKE :search2 LIMIT :skip, 15
    UNION
    SELECT id, desc FROM table WHERE desc LIKE :search3 LIMIT :skip, 15
');

$stmt->bindParam(':search1', $search1);
$stmt->bindParam(':search2', $search2);
$stmt->bindParam(':search3', $search3);
$stmt->bindParam(':skip', $current_page, PDO::PARAM_INT);

$stmt->execute();

$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

第一个查询(没有联合)可以正常工作,或者如果我删除 :skip 参数,它也可以正常工作。

有什么想法吗?

【问题讨论】:

  • desc 是保留字(table 也是保留字,但我怀疑这就是您使用的表名,对吧?wink)。它需要用反引号包裹。 dev.mysql.com/doc/refman/5.5/en/reserved-words.html --- 等待答案在此评论之后堆积...(如果我这样做,我不想陷入潜在的蠕虫罐中。 )
  • 为什么要使用带占位符的偏移量,无论如何这是默认设置。
  • 你不能只做desc LIKE :search1 OR desc LIKE :search2 OR desc LIKE :search3吗?
  • 我使用联合是因为我需要按特定顺序排列的结果(首先是完全匹配,等等,从通配符中可以看出)。表名和列名只是为了简化示例:) :skip 参数 ($current_page) 随分页而变化。

标签: php mysql sql pdo


【解决方案1】:

在 PDO 中使用命名参数,你不能多次使用同一个名字,除非你这样做:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

但如果您这样做,:skip 的值将作为带引号的字符串插入到查询中,这在 LIMIT 上下文中无效。换句话说,如下:

LIMIT '0', 15

导致语法错误,因为 LIMIT 只需要真正的整数作为其参数。

更多例子和解释,见我对Parametrized PDO query and LIMIT clause - not working的回答

所以您的选择是为每次出现的:skip 添加一个单独的参数:

$stmt = $pdo->prepare('
    SELECT id, desc FROM table WHERE desc LIKE :search1 LIMIT :skip1, 15
    UNION
    SELECT id, desc FROM table WHERE desc LIKE :search2 LIMIT :skip2, 15
    UNION
    SELECT id, desc FROM table WHERE desc LIKE :search3 LIMIT :skip3, 15
');

$stmt->bindParam(':search1', $search1);
$stmt->bindParam(':search2', $search2);
$stmt->bindParam(':search3', $search3);
$stmt->bindParam(':skip1', $current_page, PDO::PARAM_INT);
$stmt->bindParam(':skip2', $current_page, PDO::PARAM_INT);
$stmt->bindParam(':skip3', $current_page, PDO::PARAM_INT);

或者自己将值插入到查询中,引用整数。

但我同意@Class 的评论,你根本不必做 UNION。如果不出意外,'%search%' 适用于其他两种模式:'search%''%search'。您不必搜索所有三个。

【讨论】:

  • 感谢您的精彩解释,它完美运行! (由于上述原因,我需要联合。)
【解决方案2】:

作为一个更新,如果有人最终在这里尝试实现一个类似的搜索,该搜索按相关性返回结果,上面的原始查询将重复一些结果,并且每个选择的限制将跳过一些结果。

经过一些测试,以下查询似乎可以按预期完成工作:

$current_page = ($_GET['page'] - 1) * 15;

$search1 = $search;
$search2 = $search."%";
$search3 = "%".$search."%";

$pdo = DB::connection()->getPdo();

$stmt = $pdo->prepare('
    SELECT id, desc FROM table WHERE desc LIKE :search1
    UNION
    SELECT id, desc FROM table WHERE desc LIKE :search2 AND desc NOT LIKE :search1a
    UNION
    SELECT id, desc FROM table WHERE desc LIKE :search3 AND desc NOT LIKE :search2a
    LIMIT :skip, 15
');

$stmt->bindParam(':search1', $search1);
$stmt->bindParam(':search1a', $search1);
$stmt->bindParam(':search2', $search2);
$stmt->bindParam(':search2a', $search2);
$stmt->bindParam(':search3', $search3);
$stmt->bindParam(':skip', $current_page, PDO::PARAM_INT);

$stmt->execute();

$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

【讨论】:

    【解决方案3】:

    将每个SELECT 放在括号中。

    来自documentation

    要将 ORDER BY 或 LIMIT 应用于单个 SELECT,请将子句放在括住 SELECT 的括号内:

    $stmt = $pdo->prepare('
        (SELECT id, desc FROM table WHERE desc LIKE :search1 LIMIT :skip, 15)
        UNION
        (SELECT id, desc FROM table WHERE desc LIKE :search2 LIMIT :skip, 15)
        UNION
        (SELECT id, desc FROM table WHERE desc LIKE :search3 LIMIT :skip, 15)
    ');
    

    【讨论】:

      猜你喜欢
      • 2019-02-24
      • 1970-01-01
      • 2012-04-15
      • 2023-01-09
      • 2016-12-15
      • 2022-01-10
      • 2017-06-10
      • 1970-01-01
      • 2016-03-04
      相关资源
      最近更新 更多