【问题标题】:PHP - How to substitute array as host parameter in prepared statementPHP - 如何在准备好的语句中将数组替换为主机参数
【发布时间】:2016-04-13 01:16:17
【问题描述】:

我能够绑定intstrboolnull 类型的值,但我无法绑定数组类型。
我已经尝试了这两个功能,即bindValuebindParam,但它们都不起作用。 我怎样才能做到这一点?

// a helper function to map Sqlite data type
function getArgType($arg) {
    switch (gettype($arg)) {
        case 'double':  return SQLITE3_FLOAT;
        case 'integer': return SQLITE3_INTEGER;
        case 'boolean': return SQLITE3_INTEGER;
        case 'NULL':    return SQLITE3_NULL;
        case 'string':  return SQLITE3_TEXT;
        default:
            throw new \InvalidArgumentException('Argument is of invalid type '.gettype($arg));
    }
}

$sql = "SELECT * FROM table_name WHERE id IN (?)";
$params = [[10, 9, 6]]; // array of array
$dbpath = '/path/to/sqlite.sqlite';
$db = new SQLite3($dbPath, SQLITE3_OPEN_READONLY);
$stmt = $db->prepare($sql);

try {
    foreach ($params as $index => $val) {
        if (is_array($val)) {
            /************* I am stuck here  *************/
            $ok = $stmt->bindParam($index + 1, $val);
            // Using bindValue also didn't worked!
        } else {
            $ok = $stmt->bindValue($index + 1, $val, getArgType($val));
        }

        if (!$ok) {
            throw new Exception("Unable to bind param: $val");
        }
    }
} catch (Exception $ex) {
    // NO exception is thrown from bindValue() or bindParam()
    $reason = "Error in binding statement. " . $ex->getMessage();
    die($reason);
}

$result = $stmt->execute();
$data = [];
while ($row = $result->fetchArray($mode)) {
    $data[] = $row;
}
var_dump($data);

编辑:我已经尝试用参数数组中所需数量的问号替换单个 ?,但只有当我的数组少于 1000 时它才有效价值观!我认为这是 PHP 中 SQLite3 中如何准备语句的限制。

【问题讨论】:

  • 不熟悉 SQLLite,但我相信您需要在查询中的每个值处放置一个占位符(例如,在当前示例中为 3 ?s)。然后绑定数组中的每个值;或在execute 中传递数组。
  • @chris85:只有当数组的元素少于 1000 个时才会起作用。我想要一个适用于所有情况的解决方案。谢谢。
  • 您达到的极限是PHP 还是sqllite?您是否尝试获取 +1000 POST 值?
  • 它的 sqlite 限制。 sqlite.org/limits.html 见第 9 点
  • 我很好奇为什么你需要过滤超过 1000 个值...

标签: php sql sqlite


【解决方案1】:

很遗憾,这是不可能!不能绑定数组。

对您的问题最简单的解决方案如下:

  1. 为数组中的每个值创建一个占位符 (?) 的 SQL 查询
  2. 通过遍历数组来绑定每个值。

但还有其他选项(例如子 SELECT)

More information here(即使是 Java 问题,也几乎是同一个主题/问题,因为在这种情况下数据库类型无关紧要)

编辑: 通常情况下,绑定参数的 SQL 限制设置为 999,但你 can change it if you need to

【讨论】:

  • 此解决方案的限制为 999。我不能绑定超过 999 个值。关于如何在 php sqlite 中更改此默认限制的任何提示?
  • @AtulVaibhav 当然可以更改 SQLite 限制。更多信息on the official SQLite page。如果这不适合您,您可以从我的答案中的链接尝试其他解决方案。
【解决方案2】:

您不能将数组绑定为IN (?) 子句的列表。 IN 列表中的每个值都必须有其不同的占位符。

要使其动态化,首先确定值数组,然后动态构建 SQL。

这将是您的代码:

$arrayParam = [10, 9, 6];
$placeHolders = implode(',', array_fill(0, count($arrayParam), '?'));
$sql = "SELECT * FROM table_name WHERE id IN ($placeHolders) AND name = ?";
// Merge the "array" parameter values with any other parameter values
// into one non-nested array:
$params = array_merge($arrayParam, ['myname']);
// ...
    foreach ($params as $index => $val) {
        // No sub arrays allowed:
        $ok = $stmt->bindValue($index + 1, $val, getArgType($val));
        // ... etc

【讨论】:

    【解决方案3】:

    我会建议您尝试做的解决方法。将查询结果传递给临时表。

    // a helper function to map Sqlite data type
    function getArgType($arg) {
            switch (gettype($arg)) {
                case 'double':  return SQLITE3_FLOAT;
                case 'integer': return SQLITE3_INTEGER;
                case 'boolean': return SQLITE3_INTEGER;
                case 'NULL':    return SQLITE3_NULL;
                case 'string':  return SQLITE3_TEXT;
                default:
                    throw new \InvalidArgumentException('Argument is of invalid type '.gettype($arg));
            }
    }
    function getTempValues() {
    $sql = "SELECT * FROM `myTemp`";
    $params = [$in]; // array of array
    $dbpath = '/path/to/sqlite.sqlite';
    $db = new SQLite3($dbPath, SQLITE3_OPEN_READONLY);
    $stmt = $db->prepare($sql);
    $result = $stmt->execute();
        $data = [];
        while ($row = $result->fetchArray($mode)) {
            $data[] = $row;
        }
        return $data;
    }
    function addToTemp($in) {
    $sql = "SELECT * INTO `myTemp` FROM `table_name` WHERE `id` = ?";
    $params = [$in]; // array of array
    $dbpath = '/path/to/sqlite.sqlite';
    $db = new SQLite3($dbPath, SQLITE3_OPEN_READONLY);
    $stmt = $db->prepare($sql);
    if(is_array($in)) {
    foreach($in as $newValue) {
    addToTemp($newValue);
    }
    } else {
     try {
            foreach ($params as $index => $val) {
                    $ok = $stmt->bindValue($index + 1, $val, getArgType($val)); 
                if (!$ok) {
                    throw new Exception("Unable to bind param: $val");
                }
            }
        } catch (Exception $ex) {
            // NO exception is thrown from bindValue() or bindParam()
            $reason = "Error in binding statement. " . $ex->getMessage();
            die($reason);
        }
    
        $stmt->execute();
    }
     return getTempValues();
    }
    
    print_r(addToTemp([10,9,6]));
    

    【讨论】:

      【解决方案4】:

      (警告:这个答案是在删除“mysql”标签之前写的;我不知道addslashes 是否适用于 sqlite3。)

      在 PHP 中,给定 $list 作为一个指向 IN 列表的值数组:

      $list = array(1, 2, 'abcd', 'double quote: "', "apostrophe: don't");
      $ins = implode(', ', array_map(
              function($a) {
                     return "'" . addslashes($a) . "'";
                           }, $list));
      echo $sql = "... IN ($ins) ...";;
      

      产量

      ... IN ('1', '2', 'abcd', 'double quote: \"', 'apostrophe: don\'t') ...
      

      (是的,这可以通过普通的for 循环来完成,而无需使用array_map 和“匿名函数”。)

      别担心;数字列的引号 ('123') 可以用于数字列。

      【讨论】:

        【解决方案5】:

        你可以添加 ?占位符与数组中的项目数一样多

        Select * from table_a where field in (?,?,?,?,?,?,?,.....)
        

        如果它们大于 1000,则将它们拆分为两个或多个查询。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-28
          • 1970-01-01
          相关资源
          最近更新 更多