【问题标题】:Mapping PHP associative arrays into PDO prepared statements将 PHP 关联数组映射到 PDO 准备语句
【发布时间】:2017-02-16 23:41:42
【问题描述】:

我正在对数据进行一些清理和转换(该部分已完成,哇),并且需要将其插入到 MySQL 表中。之前在 Perl 中做过这种事情,我认为,作为处理的一部分,我将数据构造为关联数组是有意义的,其中键与我需要将它们加载到的字段名称相同 -这样,只需遍历键并生成命名占位符和匹配值的列表,就可以很容易地构造一个准备好的语句。

但是,我似乎无法在 PHP/PDO 中完成这项工作。测试代码:

$x = <<<EOD
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
EOD;

$fields = array('name', 'job', 'wallet_size', 'inseam', 'pet_name');
foreach(explode("\n", $x) as $line){
    $data = array_combine($fields, explode(' ', $line));

    # print_r($data);

    $stmt = $dbh->prepare('INSERT INTO foobar VALUES('.':'.implode(', :', $fields));

    foreach($fields as $field){
        $stmt->bindParam(':'.$field, $data[$field]);
    }
    $stmt->execute();
}

坦率地说,工作起来感觉太……不优雅和笨拙——但事实并非如此。

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1' in tst.php:24

有正确的方法可以做到这一点,对吧?如果有人能向我介绍适当的 PHP 风格的措辞,我将不胜感激。

【问题讨论】:

  • 您是否尝试打印'INSERT INTO foobar VALUES('.':'.implode(', :', $fields)?看起来你只是错过了关闭“)”'INSERT INTO foobar VALUES(' . ':' . implode(', :', $fields) . ')'
  • @E_p - 干得好!我确实回应了它-“内部”部分,而不是全部-看起来还不错。你是对的,它是结束括号。

标签: php mysql pdo associative-array data-munging


【解决方案1】:

当您盯着格式化 SQL 的 PHP 代码而不是最终的 SQL 字符串本身时,很难调试 SQL。我建议您始终创建一个字符串变量,以便在调试期间输出它。

$sql = 'INSERT INTO foobar VALUES('.':'.implode(', :', $fields);
echo "$sql\n";
$stmt = $dbh->prepare($sql);

输出:

INSERT INTO foobar VALUES(:name, :job, :wallet_size, :inseam, :pet_name

现在很容易看出您忘记了该 INSERT 语句末尾的结束 )!

此外,您对 PDO 的使用比实际需要的要困难得多。您不需要使用命名参数。你不需要使用bindParam()。以下是我将如何编写此代码:

$fields = array('name', 'job', 'wallet_size', 'inseam', 'pet_name');

$columns = implode(',', $fields);
$placeholders = implode(',', array_fill(1, count($fields), '?'));
$sql = "INSERT INTO foobar ($columns) VALUES ($placeholders)";
echo "$sql\n"; // use this during debugging
$stmt = $dbh->prepare($sql);

foreach(explode("\n", $x) as $line){
    $param_values = explode(' ', $line);
    $stmt->execute($param_values);
}

提示:

  • 准备一次查询,然后为要插入的每一行数据重复使用准备好的语句。
  • 将一组数据值作为参数传递给execute()。这比使用bindParam() 更容易。
  • 当您的数据位于简单数组而非关联数组中时,请使用位置参数占位符 (?) 而不是命名参数占位符。

【讨论】:

  • 美丽 - 正是我想要的。谢谢你,比尔! @E_p 发现了我错过了括号的事实,但是您的回答包括了这一点以及您阅读时有意义的内容(在我看来,我的代码没有。)非常感谢!
  • 当您扩展某些人的完美答案时(没有信用),请确保它是完美的。 $sql = "INSERT INTO foobar ($columns) VALUES ($placeholders)"; 应该是 $sql = sprintf("INSERT INTO foobar (%s) VALUES (%s)", $columns, $placeholders);
  • 我同意重写比尔。但是,使用命名占位符比? 更容易跟踪。我同时使用 MySQLi_ 和 PDO 准备语句,但在 PDO 中 99% 的时间使用命名占位符;但这只是我;-)
  • @E_p,在这种情况下,没有必要使用sprintf,使用sprintf会使代码不太清晰。
  • @Fred-ii-,当然,这是个人风格的问题。这是一个权衡。使用位置参数可以很容易地使用 array_fill() 创建占位符列表以匹配字段数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-30
  • 2014-04-19
  • 2014-03-12
  • 2012-08-05
  • 2013-04-21
  • 1970-01-01
相关资源
最近更新 更多