【问题标题】:Prepared statement making an insert with int table fields准备好的语句使用 int 表字段进行插入
【发布时间】:2014-09-04 17:15:54
【问题描述】:

我正在从事一个学习 OOP 的个人项目。我创建了这个通用方法来插入表,但我做不到。 $dbAttributes 是表中没有 id 的字段列表。我传递了一个对象实例,例如,带有 category_id、product_name、price 等属性集的产品。当我尝试使用 foreach 循环绑定值时,问题就来了。

我收到错误:

SQLSTATE[HY000]:一般错误:1366 整数值不正确

然后在我从占位符中删除单引号后,我得到:

SQLSTATE[42000]:语法错误或访问冲突:1064

public static function create($obj) {
    global $db;

    $db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
    $dbAttributes = $obj->db_fields;
    $colonValues = array();

    for($i=0;$i<count(array_values($dbAttributes));$i++){
        $colonValues[] = ':'.$dbAttributes[$i];
}

   $sql ="INSERT INTO ".$obj::$tableName."(";
   $sql .=join(",", array_values($dbAttributes));
   $sql .= ") VALUES('";
   $sql .= join("','",array_values($colonValues));
   $sql .= "')";

   try {
        $stmt = $db->prepare($sql);

        foreach($colonValues as $k=>$value){

            $rs = $obj->$dbAttributes[$k];
            //if($k!=0){
            $stmt->bindParam($value, $rs); 
            echo gettype($rs)." ". $rs."<br>";//}

    }

    var_dump($sql);
    $numRowsAffected = $db->exec($sql);

   } catch (Exception $ex) {
      echo $ex->getMessage();
   }

}

如您所见,我正在尝试动态创建 SQL 并动态绑定值。我尝试了所有方法,还阅读了有关使用表名使用反引号的论坛,但没有解决问题。

【问题讨论】:

    标签: php pdo insert prepared-statement


    【解决方案1】:

    你准备了语句,但你不执行准备好的语句。你exec($sql)作为一个未准备好的声明。此方法不支持查询参数,它逐字执行 SQL。因此,当 MySQL 需要一个整数文字并找到 :columnname 时,它会抱怨也就不足为奇了。

    $numRowsAffected = $db->exec($sql);
    

    这应该是:

    $stmt->execute();
    $numRowsAffected = $stmt->rowCount();
    

    参数占位符不能放在 SQL 中的引号内。

    另外,您不必使用 bindValue(),只需将数组传递给 execute()。

    这是编写函数的一种更简单的方法:

    public static function create($obj) {
        global $db;
    
        $db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
        $dbAttributes = $obj->db_fields;
        $columns = array_map(function ($col) { return "`" . $col . "`"; },
            array_values($dbAttributes));
        $paramPlaceholders = array_map(function ($col) { return ":" . $col; },
            array_values($dbAttributes));
        $paramValues = array_intersect_key(get_object_vars($obj), array_flip($dbAttributes));
    
        $sql ="INSERT INTO `".$obj::$tableName."` (".implode(",",$columns).")"
            . " VALUES (".implode(",",$paramPlaceholders).")";
    
        try {
            $stmt = $db->prepare($sql);
            $stmt->execute($paramValues);
            $numRowsAffected = $stmt->rowCount();
        } catch (Exception $ex) {
            echo $ex->getMessage();
        }
    
    }
    

    【讨论】:

    • 我已尝试根据您的建议修复我的代码,并且效果很好。就像给任何人的注释一样,当我使用上面的代码执行 bindParam 时,它会将所有内容都更改为字符串,因此我使用了 bindValue,并按照您的建议使用了 execute()。你的方法简短明了。我在一篇教程中读到的一个问题 exec() 在我们进行插入、更新或删除时是理想的,因为它返回受影响的行数。根据您的回答,我应该只使用 execute() 而不是 exec() 来准备语句。你是这么说的吗?您还添加了反引号;是因为占位符名称与列名匹配吗?
    • exec() 不支持prepared statements,所以如果你使用exec() 就不能有参数。我在表名和列名周围添加了反引号,以防它们与 SQL 保留字匹配。
    猜你喜欢
    • 1970-01-01
    • 2016-03-07
    • 2018-11-20
    • 1970-01-01
    • 2014-04-25
    • 2013-10-16
    • 1970-01-01
    • 2015-03-02
    • 1970-01-01
    相关资源
    最近更新 更多