MySQL Insert query becomes slow on progress - php

Im using Yii createCommand QueryBuilder and constructing below insert query, below code works like charm (inserts 6000 records in 3 secs) but after sometime its speed becomes very slow.
My code:
$taskmodel = TaskModel::model()->findAll($cri); // filtering using some criteria
$now = new DateTime();
foreach( $taskmodel as $tsk ) {
$recall_date = $tsk['recall_date'] == "" ? "null" : '"'.$tsk['recall_date'].'"';
$recall_agent = $tsk['recall_agent_id'] == "" ? "null" : $tsk['recall_agent_id'];
$next_date = $tsk['next_action_date'] == "" ? "null" : '"'.$tsk['next_action_date'].'"';
$pc_color = $tsk['postcode_color'] == "" ? "null" : '"'.$tsk['postcode_color'].'"';
$cpc_id = $tsk['crm_campaign_post_code_id'] == "" ? "null" : $tsk['crm_campaign_post_code_id'];
$priority = $tsk['priority'] == "" ? 10 : $tsk['priority'];
$field1 = "null" ;
$field2 = "null"
$field3 = "null"
$field4 = "null" ;
$contact_timezone = $tsk['contact_timezone'] == "" ? "''" : '"'.$tsk['contact_timezone'].'"';
$sql[] = '('.$tsk['crm_task_id'].', '.$tsk['crm_campaign_id'].', "'.$tsk['crm_contact_id'].'", "'.$now->format('Y-m-d H:i:s').'", "'.$now->format('Y-m-d H:i:s').'
", '.$tsk['crm_filter_id'].', '.$tsk['current_status'].', '.$priority.', '.$recall_date.', '.$recall_agent.', '.$next_date.
', '.$pc_color.', '.$cpc_id.', '.$sort.', '.$field1.', '.$field2.', '.$field3.', '.$field4.', '.$contact_timezone.', '.$tsk['is_active'].')';
}
if(sizeof($sql) > 0){
$ins ='INSERT INTO crm_pending_task (crm_task_id, crm_campaign_id,crm_contact_id,created,updated,crm_filter_id,
current_status,priority,recall_date,recall_agent_id,next_action_date,postcode_color,crm_campaign_post_code_id,sort,
field1,field2,field3,field4,contact_timezone,is_active) VALUES '.implode(',', $sql);
Yii::app()->db->createCommand($ins)->execute();
}
i found something interesting, after inserting 50000 records it becomes slow !!! why is it so ???
How to improve insert query speed till it finishes all insertion ?

I think is because your request is taking too much memory and you are swapping.
To free some memory you shouldt try to enable Gc and launch it in your foreach (maybe not at each iteration)
gc_enable();
gc_collect_cycles();

Perhaps trying inserting smaller chunks into the database.
foreach( $taskmodel as $tsk ) {
$recall_date = $tsk['recall_date'] == "" ? "null" : '"'.$tsk['recall_date'].'"';
$recall_agent = $tsk['recall_agent_id'] == "" ? "null" : $tsk['recall_agent_id'];
$next_date = $tsk['next_action_date'] == "" ? "null" : '"'.$tsk['next_action_date'].'"';
$pc_color = $tsk['postcode_color'] == "" ? "null" : '"'.$tsk['postcode_color'].'"';
$cpc_id = $tsk['crm_campaign_post_code_id'] == "" ? "null" : $tsk['crm_campaign_post_code_id'];
$priority = $tsk['priority'] == "" ? 10 : $tsk['priority'];
$field1 = "null" ;
$field2 = "null"
$field3 = "null"
$field4 = "null" ;
$contact_timezone = $tsk['contact_timezone'] == "" ? "''" : '"'.$tsk['contact_timezone'].'"';
$sql[] = '('.$tsk['crm_task_id'].', '.$tsk['crm_campaign_id'].', "'.$tsk['crm_contact_id'].'", "'.$now->format('Y-m-d H:i:s').'", "'.$now->format('Y-m-d H:i:s').'
", '.$tsk['crm_filter_id'].', '.$tsk['current_status'].', '.$priority.', '.$recall_date.', '.$recall_agent.', '.$next_date.
', '.$pc_color.', '.$cpc_id.', '.$sort.', '.$field1.', '.$field2.', '.$field3.', '.$field4.', '.$contact_timezone.', '.$tsk['is_active'].')';
}
$recordCount = sizeof($sql);
$sliceSize = 250;
for($x = 0; $x < $recordCount; $x += $sliceSize) {
$ins = sprintf('
INSERT INTO
crm_pending_task
(
crm_task_id, crm_campaign_id, crm_contact_id, created,updated,
crm_filter_id, current_status, priority, recall_date, recall_agent_id,
next_action_date, postcode_color, crm_campaign_post_code_id, sort,
field1, field2, field3, field4, contact_timezone, is_active
)
VALUES
%s; ', implode(',', array_slice($sql, $x, $sliceSize));
Yii::app()->db->createCommand($ins)->execute();
}
}

Related

Multiple variable declaration in PHP

How can I declare multiple variables in a single variable in PHP?
$dtAno = ($detData == 'yyyy') ? "to_char(noti_infrcod, 'YYYY')" : " NULL ";
$dtMes .= ($detData == 'mm/yyyy') ? "to_char(noti_infrcod, 'MM')" : " NULL ";
$dtAno .= ($detData == 'dd/mm/yyyy') ? "to_char(noti_infrcod, 'DD')" : " NULL ";
$dtHora .= ($detData == 'HH24/dd/mm/yyyy') ? "to_char(noti_infrcod, 'HH24')" : " NULL ";
$dtSelect = $dtAno.$dtMes.$dtDia.$dtHora;

Random line break in json response

I am using ajax to get a json response and fill my datatable however for some reason some random values in the json have a line break. In the database the value will be lets say "90 g per 100 m of row" but it outputs in the json like below.
All other rows are fine and here is how I make the json object.
for($i = 0; $i < $numRows; $i++) {
$chemicals = getPestsChemicals($connection, $rows[$i]['code']);
$chemNames = '';
foreach($chemicals as $chem) {
$chemNames .= $chem['chemical'].'<br>';
}
$mrls = getPestsMrls($connection, $rows[$i]['code']);
$mrlNames = '';
foreach($mrls as $mrl) {
$mrlNames .= (strlen($mrl['mrl']) == 1 ? $mrl['mrl'].".0" : $mrl['mrl']).'<br>';
}
if($i != $numRows-1) {
$body .= '
{
"crop": "'.$rows[$i]['crop'].'",
"diseases": "'.$rows[$i]['pest_name'].'",
"chemical": "'.$chemNames.'",
"product": "'.$rows[$i]['product']." ".($rows[$i]['footnote'] != NULL ? '<sup class=\"text-danger font-weight-bold\">'.$rows[$i]['footnote'].'</sup>' : NULL ).'",
"rate": "'.$rows[$i]['rate'].'",
"max_no": "'.$rows[$i]['max_no'].'",
"hi": "'.$rows[$i]['hi'].'",
"mrl": "'.$mrlNames.'",
"pcs_no": "'.($rows[$i]['pcs_no'] == 0 ? 'DR' : '0'.$rows[$i]['pcs_no']).'",
"supplier": "'.$rows[$i]['supplier'].'",
"use_by_date": "'.$rows[$i]['use_by_date'].'"
},
';
} else {
$body .= '
{
"crop": "'.$rows[$i]['crop'].'",
"diseases": "'.$rows[$i]['pest_name'].'",
"chemical": "N/A",
"product": "'.$rows[$i]['product']." ".($rows[$i]['footnote'] != NULL ? '<sup class=\"text-danger font-weight-bold\">'.$rows[$i]['footnote'].'</sup>' : NULL).'",
"rate": "'.$rows[$i]['rate'].'",
"max_no": "'.$rows[$i]['max_no'].'",
"hi": "'.$rows[$i]['hi'].'",
"mrl": "N/A",
"pcs_no": "'.($rows[$i]['pcs_no'] == 0 ? 'DR' : '0'.$rows[$i]['pcs_no']).'",
"supplier": "'.$rows[$i]['supplier'].'",
"use_by_date": "'.$rows[$i]['use_by_date'].'"
}
';
}
}
I have tried to wrap the relevant row in strval or json_encode but no success. Also here is how it looks in the db
For a quick fix try
$rate_string = (string) $rows[$i]['rate'];
then just replace:
"rate": "'.$rate_string .'",
If it fails to cast into string you might have a further clue.

how to escape empty and/or black field on PDO update?

i have a tabe with many fields and i want to change one or many fields with the same update method , the problem is when i try to update it affect the others fields that i have not updated too .
this is my sql function :
<?php
if ($_REQUEST['fct']=="ModelUpdate")
{
if ( isset( $_REQUEST['day'] ) && isset( $_REQUEST['month'] ) && isset( $_REQUEST['year'] ) ) {
$D_DATE_NAISSANCE = "".$_REQUEST['year']."/".$_REQUEST['month']."/".$_REQUEST['day']."";
}else{
$D_DATE_NAISSANCE = $_REQUEST['model_bidthday'];
}
$PK_MODEL = isset($_REQUEST['PK_MODEL']) ? $_REQUEST['PK_MODEL'] : $_SESSION['PK_MODEL'];
$K_KEY_MODEL = isset($_REQUEST['K_KEY_MODEL']) ? $_REQUEST['K_KEY_MODEL'] : $_SESSION['K_KEY_MODEL'];
$FK_STUDIO = $_REQUEST['model_studio'];
//$S_LOGIN = $_REQUEST['model_username'];
//$S_EMAIL = $_REQUEST['model_adressmail'];
//$S_PASSWORD = $_REQUEST['S_PASSWORD'];
$S_FIRSTNAME = $_REQUEST['model_firstname'];
$S_LASTNAME = $_REQUEST['model_lastname'];
//$D_DATE_NAISSANCE = $_REQUEST['model_bidthday'];
$S_GENRE = $_REQUEST['model_gender'];
$S_COUNTRY_CODE = $_REQUEST['model_coutryCode'];
$S_CITY = $_REQUEST['model_city'];
$S_ZIP = $_REQUEST['model_zipcode'];
$S_ADRESS = $_REQUEST['adress'];
$S_NATIONALITY = $_REQUEST['model_nationality'];
$S_ETHNIE = $_REQUEST['model_ethnie'];
$S_CARD_ID_FRONT = $_REQUEST['S_CARD_ID_FRONT'];
$S_CARD_ID_BACK = $_REQUEST['S_CARD_ID_BACK'];
$S_IMAGE_CAM = $_POST['S_IMAGE_CAM'];
$sql = $sqlserver->prepare("UPDATE t_model SET FK_STUDIO=? , S_FIRSTNAME=? , S_LASTNAME=? , D_DATE_NAISSANCE=?, S_GENRE=? ,S_COUNTRY_CODE=?, S_CITY=? , S_ZIP=? , S_ADRESS=? , S_NATIONALITY=? , S_ETHNIE=? , S_CARD_ID_FRONT=?, S_CARD_ID_BACK=? , S_IMAGE_CAM=? where PK_MODEL=? and K_KEY_MODEL=?");
$r = $sql->execute(array($FK_STUDIO,$S_FIRSTNAME,$S_LASTNAME,$D_DATE_NAISSANCE,$S_GENRE,$S_COUNTRY_CODE,$S_CITY,$S_ZIP,$S_ADRESS, $S_NATIONALITY, $S_ETHNIE, $S_CARD_ID_FRONT, $S_CARD_ID_BACK,$S_IMAGE_CAM, $PK_MODEL,$K_KEY_MODEL)) or die(print_r($sql->errorInfo()));
$sql->closeCursor();
echo 1;
}
?>
In case you're using an sql server (as the name of the variable suggests) you can use ISNULL(expr1,expr2). In case the parameter in the query is null (expr1) then use the current value of that row (expr2).
// using php7's Null coalescing operator
// for php < 7 use: isset($_REQUEST['key']) ? $_REQUEST['key'] : replacement
$PK_MODEL = $_REQUEST['PK_MODEL'] ?? $_SESSION['PK_MODEL'];
$K_KEY_MODEL = $_REQUEST['K_KEY_MODEL'] ?? $_SESSION['K_KEY_MODEL'];
$FK_STUDIO = $_REQUEST['model_studio'] ?? NULL;
$S_FIRSTNAME = $_REQUEST['model_firstname'] ?? NULL;
$S_LASTNAME = $_REQUEST['model_lastname'] ?? NULL;
$S_GENRE = $_REQUEST['model_gender'] ?? NULL;
$S_COUNTRY_CODE = $_REQUEST['model_coutryCode'] ?? NULL;
$S_CITY = $_REQUEST['model_city'] ?? NULL;
$S_ZIP = $_REQUEST['model_zipcode'] ?? NULL;
$S_ADRESS = $_REQUEST['adress'] ?? NULL;
$S_NATIONALITY = $_REQUEST['model_nationality'] ?? NULL;
$S_ETHNIE = $_REQUEST['model_ethnie'] ?? NULL;
$S_CARD_ID_FRONT = $_REQUEST['S_CARD_ID_FRONT'] ?? NULL;
$S_CARD_ID_BACK = $_REQUEST['S_CARD_ID_BACK'] ?? NULL;
$S_IMAGE_CAM = $_POST['S_IMAGE_CAM'] ?? NULL;
$sql = $sqlserver->prepare("
UPDATE
t_model
SET
FK_STUDIO=IsNull(?,FK_STUDIO),
S_FIRSTNAME=IsNull(?,S_FIRSTNAME),
S_LASTNAME=IsNull(?,S_LASTNAME),
D_DATE_NAISSANCE=IsNull(?,D_DATE_NAISSANCE),
S_GENRE=IsNull(?,S_GENRE),
S_COUNTRY_CODE=IsNull(?,S_COUNTRY_CODE),
S_CITY=IsNull(?,S_CITY),
S_ZIP=IsNull(?,S_ZIP),
S_ADRESS=IsNull(?,S_ADRESS),
S_NATIONALITY=IsNull(?,S_NATIONALITY),
S_ETHNIE=IsNull(?,S_ETHNIE),
S_CARD_ID_FRONT=IsNull(?,S_CARD_ID_FRONT),
S_CARD_ID_BACK=IsNull(?,S_CARD_ID_BACK),
S_IMAGE_CAM=IsNull(?,S_IMAGE_CAM)
WHERE
PK_MODEL=?
AND K_KEY_MODEL=?
");
In case you're using MySQL, the same can be done via IFNULL.
Either way it's cruical that the server really gets a NULL-value (not only an empty string but NULL).
You could try to use dynamically created queries.
You'll have to have the input fields' names the same as your columns in the table that you're going to update.
Then pass all the variables to the superglobal $_POST this way you won't update anything that is empty.
In your update function loop through $_POST like this:
$sql = 'UPDATE t_model SET ';
foreach($_POST as $key=>$value){
if($value !== '' && !empty($value)) //checking if you don't have an empty value and you can add more exceptions here by doing '&& $key !== 'exception' or '&& $value !== "exception"'
$sql .= $key.' = :'.$key.', ';
}
$sql = rtrim($sql, ",")." where PK_MODEL=:PK_MODEL and K_KEY_MODEL=:K_KEY_MODEL ";
$query = $sqlserver->prepare($sql);
foreach($_POST as $key=>$value){
if($value !== '' && !empty($value)){
$query->bindValue(':'.$key, $value);
}
}
$query->execute();
$query->closeCursor();
echo 1;
This should work, I've been using the same structure for my dynamic admin panel and it works like a charm.
NOTE: I've changed some variable names to make it a little bit easier to read for potential other users
IMPORTANT EDIT: As suggested by #SZenC this could be vulnerable to SQL injection. This would be by adding input fields manually in the source code of the form.
This can all be prevented by adding an additional check in the loops like this:
$allowed_cols = array('col1', 'col2', 'col3');
if($value !== '' && !empty($value) && in_array($key, $allowed_cols)){
So the fix for this potential SQL injection is to edit the checks in the for loops

mysql where condition not working?

I have written my php code in codeigniter but when I set $limit = 1, still i get records that have seen = 1 why??
i have written a mysql query in pure sql but the problem remains !
here is my code :
function getComment($limit)
{
while( !$this->checkFuncState('getComment') );
$this->lockFunc('getComment');
$grade = $this->session->userdata('grade');
$this->db->select('*');
$this->db->select('m_comments.id AS `ComId`');
$this->db->select('m_comments.approved AS `Comappr`');
$this->db->select('m_comments.content AS `ComContent`');
$this->db->select('m_users.hid AS `UserHid`');
$this->db->from('m_comments');
$this->db->join('m_users','m_comments.user_id = m_users.id');
if( $grade == "0" || $grade == "1" )
$this->db->where("( m_comments.approved = 0 || m_comments.approved = -15 )");
else
$this->db->where('m_comments.approved','0');
$this->db->where('m_comments.seen','0');
$this->db->limit($limit);
$q = $this->db->get();
$this->setComsSeen($q);
$this->unlockFunc('getComment');
return $q;
}

How can I rewrite this code to improve its clarity?

Could you write this 'cleaner' ? Just a simple question from a beginner:)
if(isset($_GET['tid']) && trim($_GET['tid'])!==""){
$act = 'tid';
$tid = trim($_GET['tid']);
}elseif(isset($_GET['fid']) && trim($_GET['fid'])!==""){
$act = 'fid';
$fid = trim($_GET['fid']);
}elseif(isset($_GET['mid']) && trim($_GET['mid'])!==""){
$act = 'mid';
}elseif(isset($_GET['act']) && trim($_GET['act'])!==""){
$act = trim($_GET['act']);
}else{
$act = "";
}
I would do it like this:
$tid = isset( $_GET['tid'] ) ? trim( $_GET['tid'] ) : '';
$fid = isset( $_GET['fid'] ) ? trim( $_GET['fid'] ) : '';
$mid = isset( $_GET['mid'] ) ? trim( $_GET['mid'] ) : '';
$act = isset( $_GET['act'] ) ? trim( $_GET['act'] ) : '';
if ( empty( $act ) ) // act not set, construct the act from the other GET vars
{
if ( !empty( $tid ) )
$act = 'tid';
else if ( !empty( $fid ) )
$act = 'fid';
else if ( !empty( $mid ) )
$act = 'mid';
}
edit: Of course you could make this even shorter, but the question was how it could be written to “improve its clarity”. And I understand clarity as something that makes it more easy to understand, what happens in a part of code. And I think the actual logic behind the original code gets quite clear with my solution.
I see nothing bad in your code apart from lack of indentation:
if(isset($_GET['tid']) && trim($_GET['tid'])!==""){
$act = 'tid';
$tid = trim($_GET['tid']);
}elseif(isset($_GET['fid']) && trim($_GET['fid'])!==""){
$act = 'fid';
$fid = trim($_GET['fid']);
}elseif(isset($_GET['mid']) && trim($_GET['mid'])!==""){
$act = 'mid';
}elseif(isset($_GET['act']) && trim($_GET['act'])!==""){
$act = trim($_GET['act']);
}else{
$act = "";
}
Although perhaps you could benefit from a function like this
function get_non_empty($field){
return isset($_GET[$field]) && trim($_GET[$field])!='' ? $_GET[$field] : NULL;
}
Definitely not the 'cleanest' solution, but a lot shorter:
$act = '';
foreach(array('tid', 'fid', 'mid', 'act') as $a) {
if(isset($_GET[$a]) && strlen(trim($_GET[$a])) > 0) {
$$a = trim($_GET[$act = $a]);
break;
}
}
This is nearly identical logically to what poke did (+1 for poke for beating me to it), but since we're talking about clarity I thought I'd show my take on it. I like to use FALSE instead of empty strings when it means something isn't being used. It feels like a more explicit way of saying "no". Also, I rarely use the non-bracketed version of if/else but for really short assignment statements I find it way easier to read.
$tid = isset($_GET['tid']) ? trim($_GET['tid']) : FALSE;
$fid = isset($_GET['fid']) ? trim($_GET['fid']) : FALSE;
$mid = isset($_GET['mid']) ? trim($_GET['mid']) : FALSE;
$act = isset($_GET['act']) ? trim($_GET['act']) : FALSE;
if ($act){ // act not set, construct the act from the other GET vars
if ($tid) $act = 'tid';
else if ($fid) $act = 'fid';
else if ($mid) $act = 'mid';
}
Careful with those raw GET values. You should clean those values up before processing them to make sure you are getting exactly what you want, especially if this is about to insert values to a database.
Here is one way. I would however probably do something differently with the tid,fid,mid stuff if I knew what they was intended for.
list($act,$val) = firstValidGETIn('tid','fid','mid','act');
switch($act) {
case 'act': $act = $val; break;
case null : $act = ""; break;
default : $$act = $val;
}
function firstValidGETIn()
{
foreach(func_get_args() as $key)
{
if(array_key_exists($key,$_GET) && trim($_GET[$key]))
return array($key, trim($_GET[$key]));
}
return array(null,null);
}

Categories