2

我在使用 PDO 的准备好的语句中遇到问号参数问题。我的 Query 类看起来像这样(目前,我仍在为正在使用的驱动程序添加数据限制、自定义参数过滤和自动检测支持的语句等功能):

// SQL query
class Query {
    public $attributes;

    // constructor for this object
    public function __construct() {
        if ($arguments = func_get_args()) {
            $tmp = explode(" ", current($arguments));

            if (in_array(mb_strtoupper(current($tmp)), ["ALTER", "DELETE", "DROP", "INSERT", "SELECT", "TRUNCATE", "UPDATE"], true)) {
                // classify the query type
                $this->attributes["type"] = mb_strtoupper(current($tmp));

                // get the query string
                $this->attributes["query"] = current($arguments);

                // get the query parameters
                if (sizeof($arguments) > 1) {
                    $this->attributes["parameters"] = array_map(function ($input) { return (is_array($input) ? implode(",", $input) : $input); }, array_slice($arguments, 1, sizeof($arguments)));
                }

                return $this;
            }
        }
    }
}

这是执行查询的代码片段:

$parameters = (!empty($this->attributes["queries"][$query]->attributes["parameters"]) ? $this->attributes["queries"][$query]->attributes["parameters"] : null);

if ($query = $this->attributes["link"]->prepare($this->attributes["queries"][$query]->attributes["query"], [\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY])) {
    if ($query->execute((!empty($parameters) ? $parameters : null))) {
        return $query->fetchAll(\PDO::FETCH_ASSOC);
    }
}

这就是我在测试代码中的称呼:

$c1->addQuery("lists/product-range", "SELECT * FROM `oc_product` WHERE `product_id` IN (?);", [28, 29, 30, 46, 47]);

if ($products = $c1->execute("test2")) {
    foreach ($products as $product) {
        print_r($product);
    }
}

我遇到的问题是我只看到第一个产品(它是针对普通 OpenCart 安装的测试),ID 为 28。正如您在我的代码中看到的,如果传递的参数是一个数组,它会被我的 lambda 自动检测到在 Query 类构造函数中,所以它被呈现为一个字符串,如28,29,30,46,47.

PDO 设置中是否缺少我缺少的参数?或者我正在做的事情可能存在一些错误或平台限制?我知道 PDO 在数组方面可以做的事情有一些限制,这就是为什么我预先分解所有数组以便它们像简单的字符串一样传递。

我在 SO 中看到了一些程序,它们基本上组成了查询字符串,例如,使用类似的程序动态生成WHERE product_id IN ({$marks})where但这不是我想要的(如果没有已知的替代方案,我可以求助于,但它看起来不是一个非常优雅的解决方案)。$marksstr_repeat("?", sizeof($parameters))

我的开发环境由:Windows 7 x64、PHP 5.4.13(x86,线程安全)、Apache 2.4.4(x86)和 MySQL 5.6.10 x64 组成。

任何提示将不胜感激:)

4

2 回答 2

2

Lambda 检测一个数组并从中创建逗号分隔的字符串,并且传递的参数被视为字符串,因此查询如下所示:

SELECT * FROM tbl WHERE id IN('1,2,3,4')

'1,2,3,4'是 SQL 的一个字符串值。

如果您只期望数值,则可以省略将它们作为参数添加,只需将它们放在查询中:

$a = [28, 29, 30, 46, 47];
$s = "SELECT * FROM tbl WHERE id IN(".implode(',', array_map('intval', $a)).")";

对于不同的数据类型,您必须根据需要添加尽可能多的参数占位符,并分别绑定每个参数。

于 2013-04-07T19:40:29.063 回答
2

占位符?只能替代单个文字。如果您希望IN子句接受任意数量的值,则必须为数组的每个可能长度准备一个新查询。

例如,如果你想在 array 中选择 ids [1, 2],你需要一个看起来像SELECT * FROM tbl WHERE id IN (?,?). 如果然后传入一个三项数组,则需要准备一个查询,如SELECT * FROM tbl WHERE id IN (?,?,?),依此类推。

换句话说,在您拥有要绑定到准备好的语句的数据之前,您无法确定要构建/创建什么查询。

这不是 PDO 限制,它是 SQL 数据库中准备好的查询如何工作的基础。想一想——?如果你说的是 SQL领域的数据类型,IN ?但它?代表了非标量的东西?

一些数据库具有数组类型(例如 PostgreSQL)。也许他们可以以IN <array-type>相同的方式解释IN (?,?,...),这将起作用。但是 PDO 无法发送接收数组类型的数据(没有PDO::PARAM_ARRAY),并且由于这是一个不常见且深奥的功能,因此 PDO 不太可能会这样做。

请注意,这里有一层额外的破损。一个普通的数据库,在遇到这种情况时int_id = '1,2,3,4'不会匹配任何东西,因为'1,2,3,4'不能强制转换为整数。然而,MySQL 会将其转换为整数1!这就是为什么您的查询:

$pstmt = $db->prepare('SELECT * FROM `oc_product` WHERE `product_id` IN (?)');
$pstmt->execute(array('28,29,30,46,47'));

会匹配product_id = 28。看看疯狂:

mysql> SELECT CAST('28,29,30,46,47' AS SIGNED INTEGER);
+------------------------------------------+
| CAST('28,29,30,46,47' AS SIGNED INTEGER) |
+------------------------------------------+
| 28                                       |
+------------------------------------------+
1 rows in set (0.02 sec)
于 2013-04-07T19:54:47.673 回答