I have multiple user accounts and each of them running incremental set of integer for their own transactions.
So, everytime a user open a transaction, I will query the last max digit within his transaction and plus 1.
But, sometimes I found the result is returning the maximum ID from another user transaction. Supposedly user A has the last ID = 5402, and user Z has last ID = 19201. Sometimes, the user A gets the 19202 instead of 5403.
This is my query:
SELECT MAX(CAST(id AS UNSIGNED)) as max_id FROM `transaction` WHERE `user_id` = 'A'
The transaction table is like:
id INT PK
user_id INT
... etc
This is a web application and multiple users connect simultaneously and I'm using mysql as database, and php as the programming language.
I'm using CI, here is the code I use to obtain max ID
function get_max($table, $max_col, $col_id = NULL, $id = NULL) {
if (!empty($col_id) && !empty($id)) {
$this->db->where($col_id, $id);
}
$this->db->select("MAX(CAST($max_col AS UNSIGNED)) as max_$max_col");
$query = $this->db->get($table);
if ($query->num_rows() > 0) {
return intval($query->row_array()["max_$max_col"]);
}
return 0;
}
Once I obtained the id, I insert as below:
$new_data['id'] = $this->model_share->get_max('transaction', 'id', 'user_id', $user_id) + 1;
$new_data['user_id'] = $user_id;
$this->model_share->insert('transaction', $new_data); // save data
and this is the detail of insert function
function insert($table, $data) {
$this->db->insert($table, $data);
$id = $this->db->insert_id();
if ($id <= 0)
return $this->db->affected_rows() > 0;
else return $id;
}
Codeigniter has a query function to get max value from column from database table,$this->db->select_max()
$this->db->select_max('your_column');
$query = $this->db->get('table');
And for database transaction add this before when you start any query and end it at the last query.
$this->db->trans_start();
$this->db->query('AN SQL QUERY...');
$this->db->query('ANOTHER QUERY...');
$this->db->query('AND YET ANOTHER QUERY...');
$this->db->trans_complete();
Note: Database transaction is useful when you use it when you are doing many database related query like you are inserting something and then updating just before it.
Here is the reference link
Query_builder
Transactions
Edit:
You have a syntax error in this lines because of max_$max_col this.
//Error Line1
$this->db->select("MAX(CAST($max_col AS UNSIGNED)) as max_$max_col");
//Error Line 2
return intval($query->row_array()["max_$max_col"]);
Related
when i run this code data insert in 3 tables successfully and commit when in give the error in in 3rd table insert query then data insert in 1st and 2nd table and not in 3rd table and also not RollBack function work properly. I want do when 3rd table query not work then 1st and 2nd table insert data will remove and delete.
$this->db->trans_start(TRUE);
// 1st table insert query
$this->db->insert('users',$userInfo);
$userId = $this->db->insert_id();
$query = $this->db->query("SELECT `roleId` FROM `role` WHERE roleName='Clint';");
foreach ($query->result_array() as $row)
{ $roleId = $row['roleId']; }
$user_role = array( 'user_id' => $userId, 'role_id' => $roleId );
// 2nd table insert query
$this->db->insert('user_role', $user_role);
$city_id += [ "projectInfo" => $userId ];
// 3rd table insert query
$this->db->insert('project', $projectInfo);
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE )
{
echo "Flase";
$this->db->trans_rollback();
}
else
{
echo "True";
$this->db->trans_rollback();
}
You are manually rolling back transactions! In this case, you should use
$this->db->trans_begin(); NOT $this->db->trans_start();
CI automatically rolls back transactions, you dont need do it manually.
If you use $this->db->trans_start, you dont need $this->db->trans_rollback().
Comment out $this->db->trans_rollback(), or leave those and use $this->db->trans_begin(), then check results.
https://www.codeigniter.com/user_guide/database/transactions.html
Good luck!
I have table with favorites programm. I will add with ajax query to database favorite programm id's to table with user id. How I can skip a duplicate programm id with current user id.
I now create this method for skip duplicate values but not working in calling:
public function test($programm_id){
$fav = new \App\Favorite;
$user_id = Auth::id();
$fav_count = $fav::where('id_user', Auth::user()->id)->count();
$getFavorites = $fav::where('id_user', $user_id)->get()->toArray();
$userProgramms = $fav->where('id_user', $user_id)->select('id_program')->get()->toArray();
$bool = true;
foreach ($userProgramms as $array) {
foreach ($array as $value) {
if($value === $programm_id) $bool = false;
}
}
if($fav_count <= 5){
if ($bool){
$fav->id_user = Auth::user()->id;
$fav->id_program = $programm_id;
$fav->save();
}
}
}
Here my table:
Please see my method favorite() in this controller: Controller
My updated code can't more 1 saves to database.
$fav = new \App\Favorite;
$fav_count = $fav::where('id_user', Auth::user()->id)->count();
if($fav_count <= 5)
{
$fav->updateOrInsert(['id_user' => Auth::id()], ['id_program' => $post['id_program']]);
}
Every user can add to table max 6 favorite id programms
Add an unique index on the table:
ALTER TABLE `tableName` ADD UNIQUE `unique_index`(`id_user`, `id_program`);
and use the INSERT INTO ... OR UPDATE:
INSERT INTO t1 (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
UPDATE t1 SET c=c+1 WHERE a=1;
In that manner your query will insert a new record if there isn't already a record with the same couple of id_user and id_program values, in case of it it'll perform an update on that row.
If you want to do it in PHP and assuming your values are stored in an array, you can use array_unique().
Check it out here: http://php.net/manual/en/function.array-unique.php
I have two tables in one database that are emptied and replaced very frequently. Each table has well over 65,000 rows. I want to select two fields from the first table (LnsPriceUpdates), then take those values and compare and insert them into the second table (LnsCatalog) and want to do this for all 65,000 items in the database.
(Note: I want to do all this from a localhost)
Here's the current code I'm using:
public function testbananaAction()
{
$em = $this->getDoctrine()->getManager();
$query = $em
->createQuery(
'SELECT l.partnumber, l.level1Net FROM AppBundle:LnsPriceUpdates l WHERE l.level1Net IS NOT NULL'
)
->getResult();
foreach ($query as $row){
$partNo = $row['partnumber'];
$part = $em->getRepository('AppBundle:Lnscatalog')
->findOneBy(['partnoid' => $partNo]);
$dealerCost = $row['level1Net'];
if(isset($dealerCost) && isset($part)){
$partCheck = $part->getDealerCost();
if(!isset($partCheck)){
$part->setDealerCost($dealerCost);
$em->flush();
}
} else {
continue;
}
}
return new Response('yay');
}
Any ideas to accomplish this task without crashing would be helpful.
Can you achieve with a single Sql Update something like:
update Lnscatalog c
set c.dealerCost = (SELECT l.level1Net FROM AppBundle:LnsPriceUpdates l WHERE l.level1Net IS NOT NULL and l.partnumber=c.partnoid)
where c.dealerCost is null
and exists (SELECT l.level1Net FROM AppBundle:LnsPriceUpdates l WHERE l.level1Net IS NOT NULL and l.partnumber=c.partnoid)
You can speed up this query adding some index on the above table, as example:
ALTER TABLE LnsPriceUpdates
ADD INDEX LnsPriceUpdates_IDX_1 (level1Net, partnumber) ;
ALTER TABLE LnsPriceUpdates
ADD INDEX LnsPriceUpdates_IDX_2 (partnumber) ;
ALTER TABLE Lnscatalog
ADD INDEX Lnscatalog_IDX_2 (dealerCost) ;
Some reference about optimizing sql query can be found here, here and here
Hope this help
MySQL with PHP, trying to update a row:
$dbQuery = 'UPDATE UserTable SET Age=25 WHERE Id=3';
$result = mysqli_query($dbLink, $dbQuery);
if ($result === FALSE) {
// Take care of error
}
else {
$numAffectedRows = mysqli_affected_rows($dbLink);
}
I get zero $numAffectedRows in two different cases:
1. When there is no user row with Id=3
2. When there is a user row with Id=3 but Age was already 25 before
Is there a way I can distinguish between the two cases? (apart from reading the row before and manually check the value before updating)
According to mysql documentation, you can change the behaviour of affected_rows by passing the MYSQLI_CLIENT_FOUND_ROWS flags while connecting using mysql_real_connect.
In this case, mysql_affected_rows returns the number of rows matched by the WHERE condition, not the number of updated rows.
I don't think this is possible with just one query.
I would go for INSERT INTO..ON DUPLICATE approach.
$dbQuery = "INSERT INTO UserTable (Id,Age) VALUES('3','25') ON DUPLICATE KEY
UPDATE Id='3',Age='25'";
$result = mysql_query($dbQuery);
if ($result === FALSE) {
// Take care of error
}
else {
$numAffectedRows = mysql_affected_rows();
// $numAffectedRows = 0 => row exist
// $numAffectedRows = >0 => row added with Id,Age specified
}
I want to run an update like such in PHP
// pseudocode
UPDATE invoice SET due_date=? WHERE invoice_id=? AND creater_id=?;
IF AFFECTED_ROWS == 1 THEN
UPDATE invoice_item SET price=? WHERE invoice_id=?
For added security, I appended creater_id to ensure that the code only updates if the logged in user is the invoice creator, otherwise, the system will not update.
I originally intended to check this condition using AFFECTED_ROWS. But eventually after much frustration, I realise AFFECTED_ROWS return 0 if all the new values are the same as the old values. This means that even if I have different values for the invoice_item, they will not be updated.
Other than doing a SELECT before the UPDATE, is there SQL query or PHP functions that will tell me if the UPDATE matched any row, so that I can proceeed to UPDATE invoice_item accordingly?
You can use ROW_COUNT() and if you read that it explains that when connecting to the DB you can specify the CLIENT_FOUND_ROWS flag which will give the number of rows found for the update, regardless of if they have the same value of what you're updating with.
Hope this helps.
I've taken this from my code so things like $link need to be in place- but it shows what you are interested in
function update() {
$q = "UPDATE table SET field1=? WHERE field2 = $value";
/* create a prepared statement */
$stmt = mysqli_stmt_init($link);
if (mysqli_stmt_prepare($stmt, $q)) {
mysqli_stmt_bind_param($stmt, "s", $field1);
mysqli_stmt_execute($stmt);
if(mysqli_stmt_errno($stmt)){
echo("Sql Error: ".$q. ' Sql error #: '.mysqli_stmt_errno($stmt). ' - ' . mysqli_stmt_error($stmt);
return false;
}
else{
$numrows = mysqli_stmt_affected_rows($stmt);
if (mysqli_stmt_errno($stmt) == 0 || mysqli_stmt_errno($stmt) ==''){
// numrows = -1 is flag no error and no rows affected
$numrows = ($numrows ==0?-1:$numrows);
}
else{
echo("Sql Error: ".$q. ' Sql error #: '.mysqli_stmt_errno($stmt). ' - ' . mysqli_stmt_error($stmt);
return false;
}
/* close statement */
mysqli_stmt_close($stmt);
return $numrows;
}
}
}
As per documentation on ROW_COUNT():
ROW_COUNT() returns the number of rows changed, deleted, or inserted by the last statement if it was an UPDATE, DELETE, or INSERT. For other statements, the value may not be meaningful.
Your query:
Other than doing a SELECT before the UPDATE, is there SQL query or PHP functions that will tell me if the UPDATE matched any row
You can also use ROW_COUNT() within an UPDATE or any other DDL or DML statement.
Example: Using your pseudocode:
// pseudocode
UPDATE invoice SET due_date=? WHERE invoice_id=? AND creater_id=?;
IF ( ROW_COUNT() >= 1 ) THEN
UPDATE invoice_item SET price=? WHERE invoice_id=?
END IF;
Or else, you can try like:
UPDATE invoice SET due_date=? WHERE invoice_id=? AND creater_id=?;
UPDATE invoice_item SET price=
(case when ( row_count() >= 1 ) then ? else price end)
WHERE invoice_id=?;
Before setting the parameter value check again for the row_count() value to decide whether to set values for 1 or more parameters.
You can take this back to 1 query and not worry about affected rows:
UPDATE
invoice
left join invoice_item on invoice_item.invoice_id = invoice.invoice_id
SET
invoice.due_date = ?, -- the WHERE will only let this happen if it will be changed
invoice_item.price = ? -- the WHERE will only let this happen if it will be changed
WHERE
invoice.invoice_id = ?
and invoice.creater_id = ?
and invoice.due_date != ? -- here compare the new due_date to the one already in the db