How can I use insertFromSelect(...) without 'on duplicate key update'? - php

I'm modifying Createat.php, you can see the source here.
Quick overview:
$select->from(array('o' => $this->getTable('sales/order')), $columns)
->join(array('oi' => $selectOrderItem), 'oi.order_id = o.entity_id', array())
->where('o.state NOT IN (?)', array(
Mage_Sales_Model_Order::STATE_PENDING_PAYMENT,
Mage_Sales_Model_Order::STATE_NEW
));
...
$select->where('o.channel_name IS NULL');
$adapter->query($select
->insertFromSelect($this->getMainTable(), array_keys($columns)));
So everything is good at this point. My sales_order_aggregated_created table now contains all "Website" sales totals.
$select->reset('where');
$select->where('o.channel_name = ?', 'Amazon');
$adapter->query($select
->insertFromSelect($this->getMainTable(), array_keys($columns)));
This is where my problem is, when I reset('where') and filter by another channel_name and try to insert new rows into the table the insertFromSelect() function is using "INSERT ... ON DUPLICATE KEY UPDATE" and overwrites the values inside table from the previous query.
Is there a $select->someFunction() that does not update on duplicate or where would I find the definition of insertFromSelect() function? Or, perhaps there is another way of doing this?

Varien_Db_Adapter_Interface has a constant INSERT_ON_DUPLICATE which you can pass as a flag to insertFromSelect() to disable the default ON DUPLICATE KEY UPDATE behavior.
$adapter->query(
$adapter->insertFromSelect(
$select,
$this->getMainTable(),
array_keys($columns),
Varien_Db_Adapter_Interface::INSERT_ON_DUPLICATE
)
);

Related

How to mass insert or update in a single query (not a for loop of queries) using Laravel 4.2

$cardQueryList = [];
foreach($cards as $cardName => $quantity) {
$cardQueryList[] = [
'username' => $user->username,
'card_uid' => $card->uid,
'have_quantity' => $quantity
];
}
Collection::insert($cardQueryList);
The above code creates new rows even if the row exists. How can I make it so if the row exists, it updates. And if it doesn't it creates the row? An Eloquent or Fluent answer would be optimal but I'm open to raw if there's no other way.
I would like to do a mass update/insert with a single query. Not a for loop of queries for every record. Ideally I'd like to hit the database once for obvious reasons.
Also I've already checked the following link:
Insert a new record if not exist and update if exist, laravel eloquent
The above works for a single record update/insert. Which if I ran with a for loop would be very slow. I'm looking for an answer that allows a mass insert/update in a single query.
Note: I'm using, both 'username' and 'card_uid' as my key. So basically when I find a row with said username and card_uid, I'd like to update the corresponding row. Otherwise create a new row.
Typically the sort of sql you would be using would be something along the lines of the following:-
insert into `TABLE` ( `FIELD1`,`FIELD2`, `FIELD3` ) values ( 'VALUE1','VALUE2','VALUE3' )
on duplicate key
update
`FIELD1`='VALUE1',
`FIELD2`='VALUE2',
`FIELD1`='VALUE3';
How you would use this with laravel I couldn't tell you! Oops - forgot the field names in the update part
<?php
$valuesArray = [];
foreach( $cards as $cardName => $quantity )
{
$valuesArray[] = "('".$user->username."','".$card->uid."','".$quantity."')";
}
$db->query
(
"INSERT INTO `TABLE` ( `username`,`card_uid`, `have_quantity` ) VALUES ".implode( ',', $valuesArray )."
ON DUPLICATE KEY UPDATE `have_quantity` = VALUES(`have_quantity`);"
);
Make sure you have a primary key on username and card_uid. Also don't forget to escape the values and to only run the query if $valuesArray is not empty.
If you don't want duplicates have your DB schema prevent the entry of duplicate entries. Catch the error in this case and just continue on.
You may try this:
Collection::firstOrNew([
'username' => $user->username,
'card_uid' => $card->uid,
'have_quantity' => $cards['quantity']
])
->save();
To make it working, add $fillable property with mass assignable field names in your Collection model, for example:
protected $fillable = ['username', 'card_uid', 'have_quantity'];

Update a row using idiorm and php

I have this function to update a record, but i cannot it fails and send me a "Primary key ID missing from row or is null" message, how can I fix it?
public static function update_child($data)
{
try
{
$update= ORM::for_table("dm_child",DM_TAG)
->where_equal($data["id_child"]);
$update -> set(array(
"gender" => $data["gender"]
"age_year" =>$data["year"]
"age_month" => $data["month"]
));
$update -> save();
}
catch(PDOException $ex)
{
ORM::get_db()->rollBack();
throw $ex;
}
}
Idiorm assumes that the name of the primary key is 'id', which is not that, in your case.
Therefore you have to explicitly specify it to Idiorm:
<?php
ORM::configure('id_column_overrides', array(
'dm_child' => 'id_child',
'other_table' => 'id_table',
));
See Docs>Configuration.
The answer is indeed the one provided by #iNpwd for changing the default 'id' column name for queries on a per table basis:
ORM::configure('id_column_overrides', array(
'table_name' => 'column_name_used_as_id',
'other_table' => array('pk_1', 'pk_2') // a compound primary key
));
The thing that was biting me on getting it to recognize my query was WHERE I was changing the ORM::configure values. I was not in the correct file.
A deeper link to specifically the ID Column configuration: http://idiorm.readthedocs.org/en/latest/configuration.html#id-column
I just met this problem 2 minutes ago. The real reason is, you forgot select id field in querying.
demo:
$demo = ORM::for_table('demo')->select('field_test')->find_one($id);
$demo->field_test = 'do';
$demo->save();
You will get the error.
change to :
$demo = ORM::for_table('demo')->select('field_test')->select('id')->find_one($id);
It will fix the problem.
Some tips in documents:
https://github.com/j4mie/idiorm/blob/master/test/ORMTest.php
/**
* These next two tests are needed because if you have select()ed some fields,
* but not the primary key, then the primary key is not available for the
* update/delete query - see issue #203.
* We need to change the primary key here to something other than id
* becuase MockPDOStatement->fetch() always returns an id.
*/
I've never used idiorm, so cannot guarantee that my answer will work for you, but from this page and under "Updating records", we have an example which is similar but slightly different to yours.
// The 5 means the value of 5 in the primary-key column
$person = ORM::for_table('person')->find_one(5);
// The following two forms are equivalent
$person->set('name', 'Bob Smith');
$person->age = 20;
// This is equivalent to the above two assignments
$person->set(array(
'name' => 'Bob Smith',
'age' => 20
));
// Syncronise the object with the database
$person->save();
I'm sure I'll learn the reason behind this, but let me tell you all I understand at the moment, and how I "fixed" it.
Here is the beginning of idiorm's save function:
public function save() {
$query = array();
// remove any expression fields as they are already baked into the query
$values = array_values(array_diff_key($this->_dirty_fields, $this->_expr_fields));
if (!$this->_is_new) { // UPDATE
// If there are no dirty values, do nothing
if (empty($values) && empty($this->_expr_fields)) {
return true;
}
$query = $this->_build_update();
$id = $this->id(true);
Right there, on that last line, when trying to access the $this->id, you are getting an exception thrown:
throw new Exception('Primary key ID missing from row or is null');
$this does not contain an id property. I'm not really sure how it could. The example given both on their homepage and in the docs doesn't do anything special to address this. In fact I am copying them 1:1 and still yielding the same error as you.
So, all that said, I fixed this error by just adding in my own id:
$crop = ORM::for_table('SCS_Crop')->find_one($id);
$crop->id = $id;
$crop->Name = "Foo";
$crop->save();
This also happens when the id field name is ambiguous, e.g. when joining two tables both having an id column. This is the case with referenced tables
Model::factory('tableOne')
->left_outer_join('tableTwo', array('tableOne.tableTwo_id', '=', 'tableTwo.id'))
->find_one($id);
In these cases set an alias to the ID column of the parent tableOne to later access it while saving. Make sure that you also select other columns you need - e.g. by ->select('*'):
Model::factory('tableOne')
->select('*')
->select('tableOne.id', 'id')
->left_outer_join('tableTwo', array('tableOne.tableTwo_id', '=', 'tableTwo.id'))
->find_one($id);
if in table primary key/ field name not id then following id column overrides required
default id (primary_key) to replace with other id name (primary_key)
ORM::configure('id_column_overrides', array(
'user' => 'user_id',
));
$update = ORM::for_table('user')->find_one(1);
$update->name = "dev";
try{
$update->save();
}catch(Exception $e){
echo $e;
}
print_r($update);

Eloquent update() failing due to updated_at table ambiguity

Ok, this question stems from a Laravel 4.1.23 install. I'm attempting to update multiple records using the Eloquent update() method on a query that includes a join:
ChildSchoolYear::whereNull('exit_date')->
join('school_years', 'child_school_years.school_year_id','=','school_years.id')->
update(array('child_school_years.exit_date'=>'`school_years`.`end_date`',
'child_school_years.editor_id'=>$userId))
Laravel is generating the correct SQL for the query content I'm providing above, but the full SQL statement generated is
update `child_school_years`
inner join `school_years` on `child_school_years`.`school_year_id` = `school_years`.`id`
set `child_school_years`.`exit_date` = `school_years`.`end_date`,
`child_school_years`.`editor_id` = 2,
`updated_at` = 2014-08-15 02:00:33 where `exit_date` is null)
This would work except that the automatically added updated_at field exists in both the child_school_years and school_years tables, so the addition of the field by Laravel triggers the Exception Integrity constraint violation: 1052 Column 'updated_at' in field list is ambiguous.
Any suggestions on how to domesticate the updated_at piece? I'd be happy to have the field updated, but I'll live without it if necessary should it be possible to eliminate it.
There is no way to alter Eloquent behaviour, even adjusting UPDATED_AT column won't help, so you need to use either simple Query\Builder, like already suggested, or one of the methods below, that I find a bit better:
// easiest way
ChildSchoolYear::whereNull('exit_date')
->join('school_years', 'child_school_years.school_year_id','=','school_years.id')
->getQuery() // get underlying base Query\Builder
->update(
array(
'child_school_years.exit_date' => '`school_years`.`end_date`',
'child_school_years.editor_id' => $userId,
'child_school_years.updated_at' => Carbon\Carbon::now(),
)
);
// also would work, temporary turn off auto timestamps
with($model = new ChildSchoolYear)->timestamps = false;
// above is the same as:
// $model = new ChildSchoolYear;
// $model->timestamps = false;
$model->whereNull('exit_date')
->join('school_years', 'child_school_years.school_year_id','=','school_years.id')
->update(
array(
'child_school_years.exit_date' => '`school_years`.`end_date`',
'child_school_years.editor_id' => $userId,
'child_school_years.updated_at' => Carbon\Carbon::now(),
)
);

PostgreSQL Update Issue With Quotation('')

I'm using PostgreSQL & Codeigniter. There is a table called folio in the database. It has few columns containing remarks1, remarks2, remarks3 as well. Data for the all the other columns are inserted when the INSERT statement executes for the 1st time.
When I try to execute below UPDATE statement later for the below 3 columns, remarks1 column get updated correctly. But remarks2, remarks3 columns are updated with ''.
UPDATE "folio" SET "remarks1" = 'test remark', "remarks2" = '', "remarks3" = '' WHERE "id" = '51';
Given that remarks1, remarks2, remarks3 columns data type is character varying. I'm using Codeigniter active records. At a time all 3 columns could be updated else single column could be updated depending on the user input.
What could be the issue? How can I fix this? Why columns are updated with ''?
As requested the php array in CI would be below
$data = array(
'remark1' => $this->input->post('remark1'),
'remark2' => $this->input->post('remark1'),
'remark3' => $this->input->post('remark1')
);
Function which saves the data contains below two lines only
$this->db->where('id', $folio_id);
$this->db->update('folio', $data);
Those columns are updated with '' because you tell them to?
Let's take a closer look at the query
UPDATE "folio"
SET
"remarks1" = 'test remark',
"remarks2" = '',
"remarks3" = ''
WHERE
"id" = '51';
First you select the table folio for the update.
Then you tell it to update remarks1 through remarks3 with new values. For remarks2 and remarks3 you specify to set them to an empty string. And that's what's going to happen.
Last but not least, you tell it to only apply this update to rows where id equals 51.
So, in order to only update remarks1 you can simply remove the other columns from your update:
UPDATE "folio"
SET
"remarks1" = 'test remark'
WHERE
"id" = '51';
Update:
I'm by far not a CI expert, but from what I see, I'd change the $data array to only contain information for remark1:
$data = array(
'remark1' => $this->input->post('remark1')
);
And (from my understanding) it should only update this single column.

Duplicate key error during upsert [Explanation]

I am executing the below statement in a class. This code is from
$query = array('_id' => $id, 'lock' => 0);
$update = array('$set' => array('lock' => 1));
$options = array('safe' => true, 'upsert' => true);
$result = $this->_mongo->update($query, $update, $options);
if ($result['ok'] == 1) {
return true;
}
However I do not understand how I would get a duplicate key error.
Can someone explain the possible scenarios and likelihood that I will receive this error?
I have been researching this extensively, cannot find my answer anywhere. So if it is on SO or any other website please share!
Thanks in advance.
Since you're doing an upsert and including _id in your query, you shouldn't be getting any duplicates on that key. This makes me think that you've created a unique index on lock, which isn't going to work for more than 2 documents because you only have 2 values for that field.
If you haven't put a unique index on lock, then you must have a unique index on a field you aren't showing here. That won't work either because on an insert, your upsert is going to set _id and lock only, any other field with an index will be inserted as null. If one of those fields has a unique index, then only a single document can have a null in that field. So when you try and insert another null for that field, you'll get a duplicate key error.

Categories