ON DUPLICATE KEY UPDATE [duplicate] - php

I have a CodeIgniter/PHP Model and I want to insert some data into the database.
However, I have this set in my 'raw' SQL query:
ON DUPLICATE KEY UPDATE duplicate=duplicate+1
I am using CodeIgniter and am converting all my previous in-controller SQL queries to ActiveRecord. Is there any way to do this from within the ActiveRecord-based model?
Thanks!
Jack

You can add the "ON DUPLICATE" statement without modifying any core files.
$sql = $this->db->insert_string('table', $data) . ' ON DUPLICATE KEY UPDATE duplicate=LAST_INSERT_ID(duplicate)';
$this->db->query($sql);
$id = $this->db->insert_id();
I hope it's gonna help someone.

The below process work for me in Codeigniter 3.0.6
public function updateOnDuplicate($table, $data ) {
if (empty($table) || empty($data)) return false;
$duplicate_data = array();
foreach($data AS $key => $value) {
$duplicate_data[] = sprintf("%s='%s'", $key, $value);
}
$sql = sprintf("%s ON DUPLICATE KEY UPDATE %s", $this->db->insert_string($table, $data), implode(',', $duplicate_data));
$this->db->query($sql);
return $this->db->insert_id();
}

Following the snippet linked by Pickett, I made a few modifications to:
1) Update it to use the new Query Builder (CI 3), formerly known as Active Record.
2) You can now pass an associative array (key => value) or an array of associative arrays. In the second form, it performs a multi-update.
I only tested it with mysqli, and it works well.
This piece of code goes in system/database/drivers/mysqli/mysqli_driver.php
function _duplicate_insert($table, $values)
{
$updatestr = array();
$keystr = array();
$valstr = array();
foreach($values as $key => $val)
{
$updatestr[] = $key." = ".$val;
$keystr[] = $key;
$valstr[] = $val;
}
$sql = "INSERT INTO ".$table." (".implode(', ',$keystr).") ";
$sql .= "VALUES (".implode(', ',$valstr).") ";
$sql .= "ON DUPLICATE KEY UPDATE ".implode(', ',$updatestr);
return $sql;
}
function _multi_duplicate_insert($table, $values)
{
$updatestr = array();
$keystr = array();
$valstr = null;
$entries = array();
$temp = array_keys($values);
$first = $values[$temp[0]];
foreach($first as $key => $val)
{
$updatestr[] = $key." = VALUES(".$key.")";
$keystr[] = $key;
}
foreach($values as $entry)
{
$valstr = array();
foreach($entry as $key => $val)
{
$valstr[] = $val;
}
$entries[] = '('.implode(', ', $valstr).')';
}
$sql = "INSERT INTO ".$table." (".implode(', ',$keystr).") ";
$sql .= "VALUES ".implode(', ',$entries);
$sql .= "ON DUPLICATE KEY UPDATE ".implode(', ',$updatestr);
return $sql;
}
And this goes into the /system/database/DB_query_builder.php file:
function on_duplicate($table = '', $set = NULL )
{
if ( ! is_null($set))
{
$this->set($set);
}
if (count($this->qb_set) == 0)
{
if ($this->db_debug)
{
return $this->display_error('db_must_use_set');
}
return FALSE;
}
if ($table == '')
{
if ( ! isset($this->qb_from[0]))
{
if ($this->db_debug)
{
return $this->display_error('db_must_set_table');
}
return FALSE;
}
$table = $this->qb_from[0];
}
$is_multi = false;
foreach (array_keys($set) as $k => $v) {
if ($k === $v) {
$is_multi = true; //is not assoc
break;
}
}
if($is_multi)
{
$sql = $this->_multi_duplicate_insert($this->protect_identifiers($table, TRUE, NULL, FALSE), $this->qb_set );
}
else
{
$sql = $this->_duplicate_insert($this->protect_identifiers($table, TRUE, NULL, FALSE), $this->qb_set );
}
$this->_reset_write();
return $this->query($sql);
}
Then you can do this for a single row insert/update:
$this->db->on_duplicate('table', array('column1' => 'value', 'column2' => 'value'));
Or this for a multi insert/update:
$this->db->on_duplicate('table', array(
array('column1' => 'value', 'column2' => 'value'),
array('column1' => 'value', 'column2' => 'value')
));

You can tweak the active record function with minimal addition:
DB_driver.php add inside the class:
protected $OnlyReturnQuery = false;
public function onlyReturnQuery($return = true)
{
$this->OnlyReturnQuery = $return;
}
find function query( ...and add at the very beginning:
if ($this->OnlyReturnQuery) {
$this->OnlyReturnQuery = false;
return $sql;
}
and finally in DB_active_rec.php add function:
public function insert_or_update($table='', $data=array())
{
$this->onlyReturnQuery();
$this->set($data);
$insert = $this->insert($table);
$this->onlyReturnQuery();
$this->set($data);
$update = $this->update($table);
$update = preg_replace('/UPDATE.*?SET/',' ON DUPLICATE KEY UPDATE',$update);
return $this->query($insert.$update);
}
Now you can use it as:
$this->db->insert_or_update('table',array $data);
Pros:
uses all the active record validation
Cons:
it is not the best (the most proper) way of extending the function, because if you are planning to update these files, you will have to redo the procedure.

The link to the forum thread above is broken. I don't know of a better way than just using db->query for the call, if someone has a better solution, please post that.
$result = $this->CI->db->query(
"INSERT INTO tablename (id, name, duplicate) VALUES (1, 'Joe', 1) ".
"ON DUPLICATE KEY UPDATE duplicate=duplicate+1");
I hope this helps someone looking for a solution to this.

Below is a code snippet I use everytime for update on duplicates:
$sql = "insert into table_name (id, name) values(".$id.",'".$name."') on duplicate key update id=".$id.",name='".$name."'";
//Executing queries
$result = $this->db->query($sql, array($id, $name));
Note: column id must be primary or unique

Using $this->db->query() and parameters, this is how I do it. the first 4 parameters are for the insert part and the last three parameters are repeated for the on duplicate key update part.
$sql = "insert into application_committee ".
"(application_id, member1_id, member2_id, member3_id) values (?, ?, ?, ?)".
" on duplicate key update member1_id = ?, member2_id = ?, member3_id = ?";
$this->db->query($sql, array($appid, $member_1, $member_2, $member_3,
$member_1, $member_2, $member_3);

Simply done -
$updt_str = '';
foreach ($insert_array as $k => $v) {
$updt_str = $updt_str.' '.$k.' = '.$v.',';
}
$updt_str = substr_replace($updt_str,";", -1);
$this->db->query($this->db->insert_string('table_name', $insert_array).' ON DUPLICATE KEY UPDATE '.$updt_str);

Related

How to loop multiple form input and assign into sql query in php?

public function add_employee($input)
{
$key_array = null;
$value_array = null;
$bind_array = null;
foreach ($input as $column => $value) {
if ($value) {
#$bind_array => ?, ?, ?;
#$value_array => [$value1, $value2, $value3];
#$key_array => column1, column2, column3;
}
}
$sql = "INSERT INTO ol_employee ($key_array) VALUES ($bind_array)";
$this->db->query($sql, $value_array);
}
Refer to comment in the function, how to achieve that output?
the idea is, from the input POST i get which over 27 fields, i just want to fill in into the $sql query i prepared as you can see. I don't think writing each table column manually is a good way.
im using Codeigniter 4 php framework + postgresql.
According to CodeIgniter 4 documentation, you can do this inside your loop for each employee:
$data = [
'title' => $title,
'name' => $name,
'date' => $date
];
$db->table('mytable')->insert($data);
You can use array and implode function first make array of key_array, value_array and bind_array then use implode() and use in sql like following
public function add_employee($input)
{
$key_array = array();
$value_array = array();
$bind_array = array();
foreach ($input as $column => $value) {
if ($value) {
$bind_array[] = '?';
$value_array[] = $value;
$key_array[] = $column;
}
}
$binds = implode(",",$bind_array);
$keys = implode(",",$key_array);
$values = implode(",",$value_array);
$sql = "INSERT INTO ol_employee ($keys) VALUES ($binds)";
$this->db->query($sql,$values);
}
by the insight of Alex Granados, this is the query i use:
public function add_employee($input)
{
foreach ($input as $column => $value) {
if ($value) {
$data[$column] = $value;
}
}
$this->db->table('ol_employee')->insert($data);
}
this will eliminate null field and regardless how many field, still working good.
as long the input name field from form is same as db column. Else, need to do some changes on that.
Thanks guys.

php update to mysql with array not updating

I have a function that should be updating a row, no errors what so ever in the logs. From what I see it should be working. I took the function from a update user function and tried to mimic it for the new feature.
Here is the data array posting to it.
$data = array('id' => $vid, 'name' => $vname, 'logo' => $vlogo, 'info' => $vinfo, 'site' => $vsite, 'est' => $vest);
The post works, I am doing a dump on the updatecompany page. So they do get set. I think it may be with the function. Any insight would be wonderful!
public static function updateCompany($toUpdate = array(), $company = null){
self::construct();
if( is_array($toUpdate) && !isset($toUpdate['id']) ){
if($company == null){
echo "No company ID set!";
}
$columns = "";
foreach($toUpdate as $k => $v){
$columns .= "`$k` = :$k, ";
}
$sql = self::$dbh->prepare("UPDATE companys SET {$columns} WHERE `id` = :id");
$sql->bindValue(":id", $company);
foreach($toUpdate as $key => $value){
$value = htmlspecialchars($value);
$sql->bindValue(":$key", $value);
}
$sql->execute();
}else{
return false;
}
}
$vid = $_POST["idnum"];
$vname = $_POST["name"];
$vlogo = $_POST["logo"];
$vinfo = $_POST["info"];
$vsite = $_POST["site"];
$vest = $_POST["est"];
I would maybe try using the bind array into your execute() method since you are just binding the values (I assume this is PDO). Another feature is to use an array to assemble the column portions and implode at the time of use.
public static function updateCompany($toUpdate = array(), $company = null)
{
# Also this may supposed to be: self::__construct(); ?? Notice the "__" before "construct"
self::construct();
if(is_array($toUpdate) && !isset($toUpdate['id'])) {
if(empty($company)){
# Throw exception, catch it in a parent wrapper
throw new \Exception("No company ID set!");
# Stop, no sense in continuing if an important part is missing
return;
}
foreach($toUpdate as $k => $v){
$bKey = ":{$k}";
# I would create a bind array here
$bind[$bKey] = $value;
# I generally save to an array here to implode later
$columns[] = "`{$k}` = {$bKey}";
}
# Add the id here
$bind[":id"] = $company;
# I would use a try here for troubleshooting PDO errors
try {
# Create sql with imploded columns
$sql = self::$dbh->prepare("UPDATE companys SET ".implode(",",$columns)." WHERE `id` = :id");
# Throw the bind array into the execute here
$sql->execute($bind);
}
catch(\PDOException $e) {
# I would only die here to see if there are any errors for troubleshooting purposes
die($e->getMessage());
}
} else {
return false;
}
}
Your update SQL could not work because you have comma in update value set.
See, you attached comma without any consideration:
$columns = "";
foreach($toUpdate as $k => $v){
$columns .= "`$k` = :$k, ";
}
Then the final SQL will look something like this:
UPDATE companys SET `name`=:name, `logo`=:logo, WHERE `id`=:id
Do you notice the comma just before WHERE? it should not be there!
So you should update the code like following:
$columns = "";
foreach($toUpdate as $k => $v){
if ($columns != "") $columns .= ",";
$columns .= "`$k` = :$k ";
}
This should be working.
or to no need to be aware of last comma try this:
$columns = array();
foreach($toUpdate as $k => $v){
$columns[] = "`$k` = :$k";
}
$sql = self::$dbh->prepare("UPDATE `companys` SET ".implode(',', $columns)." WHERE `id` = :id");

php array keys array values insert

Update:
Index page is performs the action of the function,
Below is my current code, above this i have a fetch all which works well, but this insert isnt working as it doesn't insert into the database, is it because i need an update all after it before i can test insert?
<?php
function insert(array $values, $tablename)
{
$key = "";
$val = "";
foreach($values as $keys=>$record){
if($keys == ""){
$key .= $keys;
}
else{
$key .= ','.$keys;
}
if($val == ""){
$val .= $record;
insert into $tablename($keys)values($val);
}
else{
$val .= ','.$record;
insert into $tablename($keys)values($val);
}
}
}
?>
Here you have few mistakes
function insert(array $values, $tablename)
$sql = "INSERT INTO $tablename ("$vaules". implode(",", array_keys($pr1)) .") VALUES ('$vaules$, ". implode(",", array_values($pr1)) ")";
Maybe you want something like this
function insert(array $values, $tablename){
$sql = "INSERT INTO $tablename (".implode(",", array_keys($values)).") VALUES ('".implode("','",array_values($values))."')";
}
You are using some $pr1 value that do not exist in function. But also I woul like that you will do something with this query in this function.
let me to be specific.
for example you have a table employee with emp_id, emp_first_name,emp_last_name and email_id fields.
and you have array for values like this
$values = array(
emp_first_name => John,
emp_last_name => Malick,
email_id => john#gmail.com
)
now call insert function from index page using below code
first of all include file in which you placed a insert function.
for example in File1.php you placed a insert function then
include 'File1.php';
$result = insert('employee',$values);
now in insert function,
function insert($tablename,$values){
foreach($values as $keys=>$record){
$key_array[] = $keys;
$myvalue = $record;
if($record == ''){
$value_array[] = "'NULL'";
}else{
$value_array[] = "'".$myvalue."'";
}
}
$keys = implode(",", $key_array);
$values = implode(",", $value_array);
insert into $tablename($keys)values($val);
}

Parsing Dictionary Object and Creating MySQL Table through PHP

Currently, I have this dictionary object in my PHP script that was posted to my PHP implemented server through my iOS app:
{"command":"save","classname":"GameScore","cheatMode":"0","playerName":"SeanPlott","score":"1337"}
Now I need to create a MySQL table based off the data...
Here is my attempt to parse the data but all i get are the objects, not the keys...
foreach( $text as $stuff ) {
if( is_array( $stuff ) ) {
echo json_encode($stuff);
foreach( $stuff as $thing ) {
echo json_encode($thing);
}
} else {
echo json_encode($stuff);
}
}
Heres the result... "save""GameScore""0""SeanPlott""1337"
This is what I plan on making as a query
$result = query("INSERT INTO '%s'('%s', '%s', '%s', '%s')
VALUES('%s','%s','%s','%s')", $classname, $key1, $key2, $key3, $key4,
$object1, $object2, $$object3, $object4);
But the query should be dynamic, since the dictionary object can have 1 or many keys/objects...
So I figure I need to implement a parsing for loop that generates the query within the array and outputs a string which is the query...
Anyone have any ideas on how to fix my parser?
EDIT: heres my query function to handle MYSQL
function query() {
global $link;
$debug = false;
$args = func_get_args();
$sql = array_shift($args);
for ($i = 0; $i < count($args); $i++) {
$args[$i] = urldecode($args[$i]);
$args[$i] = mysqli_real_escape_string($link, $args[$i]);
}
$sql = vsprintf($sql, $args);
if ($debug) print $sql;
$result = mysqli_query($link, $sql);
if (mysqli_errno($link) == 0 && $result) {
$rows = array();
if ($result !== true)
while ($d = mysqli_fetch_assoc($result))
array_push($rows,$d);
return array('result' => $rows);
} else {
return array('error' => 'Database error');
}
}
EDIT: Took me a while and a lot of help but I completed it... It takes care of the case of spaces in values and adds '`' characters around the keys if there corresponding value contains a space...
$raw_data = '{"command":"save","classname":"GameScore","cheatMode":"0","playerName":"Sean Plott","score":"1337"}';
$data = json_decode($raw_data, true);
$columns = array_slice(array_keys($data), 2);
array_shift($data);
$table_title = array_shift($data);
$values = array();
$num = 0;
foreach($data as $key => $value) {
if(strpos($value, " ") !== false) $columns[$num] = "`".$columns[$num]."`";
$num = $num + 1;
$values[] = (!is_numeric($value)) ? "'".$value."'" : $value;
}
$final_statement = "INSERT INTO " . $table_title . " (".implode(', ', $columns).") VALUES (".implode(', ', $values).")";
echo $final_statement;
If anyone sees any way to optimize this or make it cleaner... please feel free to post something!
Thanks again for everyones input!
You could just use a simple json_decode() and implode() to format it and prepare to insertion. Consider this example:
$raw_data = '{"command":"save","classname":"GameScore","cheatMode":"0","playerName":"SeanPlott","score":"1337"}';
$data = json_decode($raw_data, true);
$columns = array_keys($data); // get the columns
$final_statement = "INSERT INTO `table` (".implode(', ', $columns).") VALUES ('".implode("','", $data)."')";
echo $final_statement;
// outputs: INSERT INTO `table` (command, classname, cheatMode, playerName, score) VALUES ('save','GameScore','0','SeanPlott','1337')
EDIT: Suppose your columns has int types (especially the ones that has numeric values). For whatever reason it doesn't work, because of a mismatch, if so, you could probably do this. Consider this example:
$raw_data = '{"command":"save","classname":"GameScore","cheatMode":"0","playerName":"SeanPlott","score":"1337"}';
$data = json_decode($raw_data, true);
$columns = array_keys($data); // get the columns
$values = array();
foreach($data as $key => $value) {
// if not numeric, add quotes, if not, leave it as it is
$values[] = (!is_numeric($value)) ? "'".$value."'" : $value;
}
$final_statement = "INSERT INTO `table` (".implode(', ', $columns).") VALUES (".implode(', ', $values).")";
echo $final_statement;
// outputs: INSERT INTO `table` (command, classname, cheatMode, playerName, score) VALUES ('save', 'GameScore', 0, 'SeanPlott', 1337)
// Note: numbers has no qoutes

PHP PDO simple insert or update function

In trying to create a simple PHP PDO update function that if the field is not found would insert it, I created this little snippet.
function updateorcreate($table,$name,$value){
global $sodb;
$pro = $sodb->prepare("UPDATE `$table` SET value = :value WHERE field = :name");
if(!$pro){
$pro = $sodb->prepare("INSERT INTO `$table` (field,value) VALUES (:name,:value)");
}
$pro->execute(array(':name'=>$name,':value'=>$value));
}
It does not detect though if the update function is going to work with if(!$pro); How would we make this one work.
You are assigning $pro to the prepare, not the execute statement.
Having said that, if you are using mysql you can use the insert... on duplicate key update syntax.
insert into $table (field, value) values (:name, :value) on duplicate key update value=:value2
You can't use the same bound param twice, but you can set two bound params to the same value.
Edit: This mysql syntax will only work where a key (primary or another unique) is present and would cause an insert to fail.
If it's mysql-only you could try INSERT INTO ... ON DUPLICATE KEY UPDATE
http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
You will first need to execute it.
Apart from that, this is a dodgy way of doing this. It would be better to start a transaction, do a SELECT and then determine what to do (INSERT or UPDATE). Just checking whether the UPDATE query succeeded doesn't suffice, it succeeds when no row is found too.
try,
PDO::exec()
returns 1 if inserted.
2 if the row has been updated.
for prepared statements,
PDOStatement::execute()
You can try,
PDOStement::rowCount()
The following are PHP PDO helper functions for INSERT and UPDATE
INSERT function:
function basicInsertQuery($tableName,$values = array()){
/*
//
USAGE INSERT FUNCTÄ°ON
$values = [
"column" => $value,
];
$result = basicInsertQuery("bulk_operations",$values);
*/
try {
global $pdo;
foreach ($values as $field => $v)
$vals[] = ':' . $field;
$ins = implode(',', $vals);
$fields = implode(',', array_keys($values));
$sql = "INSERT INTO $tableName ($fields) VALUES ($vals)";
$rows = $pdo->prepare($sql);
foreach ($values as $k => $vl)
{
$rows->bindValue(':' . $k, $l);
}
$result = $rows->execute();
return $result;
} catch (\Throwable $th) {
return $th;
}
}
UPDATE function:
function basicUpdateQuery($tableName, $values = array(), $where = array()) {
/*
*USAGE UPDATE FUNCTÄ°ON
$valueArr = [ column => "value", ];
$whereArr = [ column => "value", ];
$result = basicUpdateQuery("bulk_operations",$valueArr, $whereArr);
*/
try {
global $pdo;
//set value
foreach ($values as $field => $v)
$ins[] = $field. '= :' . $field;
$ins = implode(',', $ins);
//where value
foreach ($where as $fieldw => $vw)
$inswhere[] = $fieldw. '= :' . $fieldw;
$inswhere = implode(' && ', $inswhere);
$sql = "UPDATE $tableName SET $ins WHERE $inswhere";
$rows = $pdo->prepare($sql);
foreach ($values as $f => $v){
$rows->bindValue(':' . $f, $v);
}
foreach ($where as $k => $l){
$rows->bindValue(':' . $k, $l);
}
$result = $rows->execute();
return $result;
} catch (\Throwable $th) {
return $th;
}
}

Categories