PHP和MySQL - 如果在特定列上为NULL,则REPLACE INTO

时间:2014-06-15 12:09:57

标签: php mysql sql

我正在尝试使用REPLACE INTO来更改列的值,如果它是NULL。

以下是代码的一部分:

  // Set query  
  $this->db->query('REPLACE INTO sessions VALUES (:id, :access, :data, :identifier)');

  // Bind data
  $this->db->bind(':id', $id);
  $this->db->bind(':access', $access);  
  $this->db->bind(':data', $data);
  $this->db->bind(':identifier', $rnd_id);

它是PDO的一部分,所以我一直在绑定变量以帮助防止SQL注入。

我只想在列为NULL时才替换'identifier',但我不确定SQL查询应该是什么。

修改
只是提供更多信息。上面的当前代码用于创建会话表,其中包含会话ID,上次访问会话,会话数据和唯一标识符。标识符最终将用于客户端cookie。

执行查询时,会话行会更新。 id保持不变,访问和数据更新。 我需要一种方法来更新id / access / data,但只有在它为null时才更新标识符。

以上代码基于本教程: http://culttt.com/2013/02/04/how-to-save-php-sessions-to-a-database/

我正在努力适应。

到目前为止,这是我所拥有的一切:

database.class.php
这提供了PDO连接和功能

<?php
class Database{
    private $host      = DB_HOST;
    private $user      = DB_USER;
    private $pass      = DB_PASSWORD;
    private $dbname    = DB_NAME;
    private $stmt;

    private $dbh;
    private $error;

    public function __construct(){
        // Set DSN
        $dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
        // Set options
        $options = array(
            PDO::ATTR_PERSISTENT    => true,
            PDO::ATTR_ERRMODE       => PDO::ERRMODE_EXCEPTION
        );
        // Create a new PDO instanace
        try{
            $this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
        }
        // Catch any errors
        catch(PDOException $e){
            $this->error = $e->getMessage();
        }
    }

    public function query($query){
       $this->stmt = $this->dbh->prepare($query);
    }

    public function bind($param, $value, $type = null){
       if (is_null($type)) {
           switch (true) {
               case is_int($value):
                   $type = PDO::PARAM_INT;
                   break;
               case is_bool($value):
                   $type = PDO::PARAM_BOOL;
                   break;
               case is_null($value):
                   $type = PDO::PARAM_NULL;
                   break;
               default:
                   $type = PDO::PARAM_STR;
           }
       }
       $this->stmt->bindValue($param, $value, $type);
   }

   public function execute(){
      return $this->stmt->execute();
   }

   public function resultset(){
      $this->execute();
      return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
   }

   public function single(){
       $this->execute();
       return $this->stmt->fetch(PDO::FETCH_ASSOC);
   }

   public function rowCount(){
       return $this->stmt->rowCount();
   }

   public function lastInsertId(){
       return $this->dbh->lastInsertId();
   }

   public function beginTransaction(){
      return $this->dbh->beginTransaction();
   }

   public function endTransaction(){
       return $this->dbh->commit();
   }

   public function cancelTransaction(){
       return $this->dbh->rollBack();
   }

   public function close(){
       return $this->dbh=NULL;
   }
}
?>

session.php文件
此代码替换PHP默认会话处理,并将数据保存到会话表,其中包含列id(会话ID),访问权限(上次访问时间)和数据(会话数据)。

include_once('database.class.php');


class Session {

  /**
   * Db Object
   */
  private $db;




public function __construct(){
  // Instantiate new Database object

  $this->db = new Database;

  // Set handler to overide SESSION
  session_set_save_handler(
    array($this, "_open"),
    array($this, "_close"),
    array($this, "_read"),
    array($this, "_write"),
    array($this, "_destroy"),
    array($this, "_gc")
  );

  // Start the session
  session_start();
}



/**
 * Open
 */
public function _open(){
  // If successful
  if($this->db){
    // Return True
    return true;
  }
  // Return False
  return false;
}

/**
 * Close
 */
public function _close(){
  // Close the database connection
  // If successful
  if($this->db->close()){
    // Return True
    return true;
  }
  // Return False
  return false;
}

/**
 * Read
 */
public function _read($id){
  // Set query
  $this->db->query('SELECT data FROM sessions WHERE id = :id');

  // Bind the Id
  $this->db->bind(':id', $id);

  // Attempt execution
  // If successful
  if($this->db->execute()){
    // Save returned row
    $row = $this->db->single();
    // Return the data
    return $row['data'];
  }else{
    // Return an empty string
    return '';
  }
}

/**
 * Write
 */
public function _write($id, $data){

   //set the random id length 
   $random_id_length = 10; 

   //generate a random id encrypt it and store it in $rnd_id 
   $rnd_id = crypt(uniqid(rand(),1)); 

   //to remove any slashes that might have come 
   $rnd_id = strip_tags(stripslashes($rnd_id)); 

   //Removing any . or / and reversing the string 
   $rnd_id = str_replace(".","",$rnd_id); 
   $rnd_id = strrev(str_replace("/","",$rnd_id)); 

   //finally I take the first 10 characters from the $rnd_id 
   $rnd_id = substr($rnd_id,0,$random_id_length);   


  // Create time stamp
  $access = time();

  // Set query  
  $this->db->query('REPLACE INTO sessions VALUES (:id, :access, :data, :identifier)');


  // Bind data
  $this->db->bind(':id', $id);
  $this->db->bind(':access', $access);  
  $this->db->bind(':data', $data);
  $this->db->bind(':identifier', $rnd_id);

  // Attempt Execution
  // If successful
  if($this->db->execute()){
    // Return True
    return true;
  }

  // Return False
  return false;
}

/**
 * Destroy
 */
public function _destroy($id){
  // Set query
  $this->db->query('DELETE FROM sessions WHERE id = :id');

  // Bind data
  $this->db->bind(':id', $id);

  // Attempt execution
  // If successful
  if($this->db->execute()){
    // Return True
    return true;
  }

  // Return False
  return false;
} 

/**
 * Garbage Collection
 */
public function _gc($max){
  // Calculate what is to be deemed old
  $old = time() - $max;

  // Set query
  $this->db->query('DELETE * FROM sessions WHERE access < :old');

  // Bind data
  $this->db->bind(':old', $old);

  // Attempt execution
  if($this->db->execute()){
    // Return True
    return true;
  }

  // Return False
  return false;
}
}
?>

我在表格中添加了一个新列'标识符'。我还在$rnd_id函数中添加了_write。这会生成10个字符的唯一标识符。

当第一次运行查询$this->db->query('REPLACE INTO sessions VALUES (:id, :access, :data, :identifier)');时,我可以在数据库中看到一个新行,其中所有4列都按预期填充。 后续执行会出现问题 - 虽然:id保持不变,但标识符将使用新的10个字符代码进行更新。我希望防止这种情况发生,并且只在第一次添加代码。后续执行应保留标识符,或者用相同的值替换它。

2 个答案:

答案 0 :(得分:0)

你想做这样的事吗?

UPDATE sessions
    set id = :id,
        access = :access,
        data = :data,
        identifier = :identifier
    where identifier is null and id = :id;

编辑:

我认为你想要的是on duplicate key update

insert into sessions(id, access, data, identifier)
    select :id, :access, :data, :identifier
    on duplicate key update access = :access, data = :data;

您需要确保id是主键或唯一键。你可以这样做:

create unique index idx_sessions_id on sessions(id);

答案 1 :(得分:-1)

您可以随时更新列accessdata,如果identifier选择了NULL,则可以更新replace into sessions select :id, :access, :data, coalesce(identifier, :identifier) from sessions where id = :id

id

尽管如果没有给定{{1}}的现有行,这会中断。如果需要插入值,当没有这样的行时,如果替换失败,则必须执行插入。

请参阅coalesce