How to insert ID +1 and get last id? - php

I want to insert id and + 1 insert the form fields into a MySQL table. I want to get the last id for the insert operation as the return value of my query but I have some problems with it.
this is my code for get last id.
function GenIDInv()
{
$CI = get_instance();
$CI->db->select('n_id');
$CI->db->from('sltax_notification_name');
$CI->db->order_by("n_id", "desc");
$query = $CI->db->get();
$result = $query->row();
if(!empty($result)){
$result = $result->n_id;
$rid = substr($result,6,9);
$id = $rid+1;
echo date('Ym') . sprintf("%'.03d\n",$id);
}
}
when i insert id it not +1

What i understood is that you want 201812001 to 201812002
Try this:
$rid = substr($result, 8, 1);
//echo $rid; // $rid should contains 1
$id = $rid+1;

Codeigniter documents: http://codeigniter.org.cn/user_guide/database/query_builder.html?highlight=max#CI_DB_query_builder::select_max
SELECT Max(n_id) FROM sltax_notification_name;

I make an assumption that your field n_id is compose like this : [YYYYMM][XXX] => [DATE][ID]
first question what happens if your id is greater than 999 ?
i will assumpt that you want to handle id greater than 999.
So now, the problem we need to resolve is how to add 1 to the id part, and keep it padded with 0 on the left.
You get your last id from database that's nice, it is probably a string.
The first thing we want to do is get the id part separate from date part.
$n_id = $result->n_id;
$id = substr($n_id, 6, strlen($n_id)) // we know the first 6 characters are part of the date and the rest is part of id
then we want to add one to $id
$id += 1; // that statement will cast our string to int
#Now we want to cast it to string and pad with 0 if needed
if (strlen($id) < 3) {
$id = str_pad($id, 3, '0', STR_PAD_LEFT);
}
Now, we just have to stick the piece together
$date = new Datetime();
$newN_id = $date->format('Ym').(string)$id

Related

Get last number from unique ID from database SQL - PHP CodeIgniter

scroll down for the answer
I have Registration table like this:
table
Now I'm hopping to get the last digit, for example:
SG20160412001 >> 1
SG20160412056 >> 56
SG20160412121 >> 121
The purpose is for generating new Reg_ID, for example:
If the returned data is = 1 then the new Reg_ID will be SG20160412002
If the returned data is = 56 then the new Reg_ID will be SG20160412057
If the returned data is = 0 then the new reg_ID will be SG20160412001
Here's my current code:
public function generate_no_reg($nmNegara)
{
$dtNow = date('Y-m-d');
$query = 'SELECT COUNT(Reg)ID) FROM Registratrion WHERE Reg_Date="$dtNow"';
$lastid = $this->db->query($query);
$id = $lastid->num_rows()+1;
$regCount = format_no_registrasi($id);
$kode = $this->get_negara($nmNegara).$regCount;
return $kode;
}
And here's my current code for generating the last 3 digit:
function format_no_registrasi($no)
{
$leadingzeros = '000';
$no_reg = date('Y') . date('m') . substr($leadingzeros, 0, (-strlen($no))) . $no+1;
return $no_reg;
}
The problem I'm having from this code is:
When i delete a row, the Primary Key will be doubled, example:
SG20160412001 << I delete this one
SG20160412002
Now the data count is returning 1, and when I generate new one it will be: SG20160412002
Any help is very much appreciated. Sorry for the problem.
scroll down for the answer
This query cuts 3 last signs from Reg_ID and returns max value.
select max(cast(substr(Reg_ID, -3) as UNSIGNED))
from FROM Registratrion
WHERE Reg_Date="$dtNow"
So id numeration supports ids till 999
Max' solution is much cleaner.
Don't use the count, use the highest number.
Create the leading characters of the Reg_ID
$leadingCharacters = 'SG' + date('Ymd');
Then select the hightes Reg_ID that starts with this string:
select Reg_ID from Registration where Reg_ID like '$leadingCharacters%' order by Reg_ID desc limit 0,1
Then extract the last three digits from the result, convert it to int and increment it. This will be your next ID.
$number = (int) substr($result,-3);
$number++;
NOTICE: Typed the code from memory, so there might be a little error in it. But the concept should work though
Have you considered splitting out your reg_id into three columns?
prefix CHAR(2)
reg_date DATE
postfix SMALLINT UNSIGNED
You can use a surrogate or composite PRIMARY if you need and concat the columns on select:
SELECT CONCAT(prefix, reg_date, LPAD(post_fix,3,'0')) reg_id
FROM registration
Keeping separate data in separate fields saves a lot of headache with operations like this.
My answer is helped by Markus and Max
This is the function that i used for leading characters, since the 'SG' is from database:
public function get_negara($nmNegara)
{
$this->db->select('Kode_Negara');
$this->db->from('tb_negara_tujuan');
$this->db->where('Nama_Negara',$nmNegara);
$query = $this->db->get();
$result = $query->row();
return $result->Kode_Negara . date('Ymd');
}
// this will returning 'SG20160413'
This is the helper function used to convert 1 to 001:
function format_no_registrasi($no)
{
$leadingzeros = '000';
$no_reg = substr($leadingzeros, 0, (-strlen($no))) . $no;
return $no_reg;
}
This is the function used to generate the full Reg_ID:
public function generate_no_reg($nmNegara)
{
$leadingChar = $this->get_negara($nmNegara);
$this->db->select_max('No_Registrasi');
$this->db->like('No_Registrasi',$leadingChar);
$query = $this->db->get('tb_registrasi');
$result = $query->row();
$noreg = (int) substr($result->No_Registrasi,-3);
$noreg++;
return $kode = $leadingChar.format_no_registrasi($noreg);
}

Unique Slug Issue Auto Increment

I have slugs formatted like my-cool-slug however I need to auto increment some to make a similar slug unique such as my-cool-slug-2 -3, -4 etc. I am following this post https://stackoverflow.com/a/15972027/635027 to do this but I ran into an issue.
The slug stops at a -1 count when inserting into MySQL and keeps entering that over an over instead of increment to the next integer when finding a duplicate. Below is my query and associated lines. What am I doing wrong?
$slug = 'my-cool-slug';
$slug_query = "SELECT * FROM pv_events WHERE event_slug LIKE '".$slug."%'";
$slug_result = mysqli_query($conn,$slug_query);
$slugs = mysqli_fetch_array($slug_result,MYSQLI_ASSOC);
if(mysqli_num_rows($slug_result) !== 0 && in_array($slug, $slugs)){
$max = 0;
//keep incrementing $max until a space is found
while(in_array( ($slug . '-' . ++$max ), $slugs) );
//update $slug with the appendage
$slug .= '-' . $max;
}
mysqli_fetch_array selects the next row, not the entire result set, so $slugs would only contain the first result.
Secondly, if you want to start with 2, it would make sense to set $max to 1, not 0. The first iteration of your while, 1 will be added.
Just select "event_slug" instead "*"
$slug_query = "SELECT event_slug FROM pv_events WHERE event_slug LIKE '".$slug."%'";
Use mysqli_fetch_all
$slugs = mysqli_fetch_all($slug_result,MYSQLI_ASSOC);

PHP SQL Update array

I originally was satisfied with the following in order to update row 1 and row 2 to the same value (status=1)
if ($_POST){
$sql ="UPDATE table SET status = 1,
WHERE id IN (1,2 );";
db()->query($sql);
if(db()->query($sql)){
echo "<b>Good</b>";
}
else{
echo "<b>No Good</b>";
}
}
But now I want to update with different values, ie- row 1 to status 1, row 2 to status 2, and row 3 to status 3.
Off the bat, I know I need to
1. Use an array and loop through it three times.
2. pass in the array value into the $sql
I figure it would be something like this but I am still learning PHP..
$array_id = array(1, 2, 3);
$array_status = array(1, 2, 3);
if ($_POST){
$sql ="UPDATE table SET status = $array_status
WHERE id = $array_id;";
db()->query($sql);
if(db()->query($sql)){
echo "<b>Update Successful</b>";
}
else{
echo "<b>Update Unsuccessful</b>";
}
}
How would I go about making this happen?
You can loop through the arrays using a for loop and exec a query for each one (Radu Vlad answer), or you can build a long query and execute it once, something like this:
if ($_POST){
$sql = ""; // Blank string
$len = count($array_id); // Number of iterations
for ($i = 0; $i < $l; $i++) { // Enter the loop
$sql .= "UPDATE
table
SET
status = {$array_status[$i]}
WHERE id = {$array_id[$i]};"; // Append the query
}
db()->query($sql);
if(db()->query($sql)){
echo "<b>Update Successful</b>";
}
else{
echo "<b>Update Unsuccessful</b>";
}
}
When the val of $i is 0, then $array_id[$i] will print the first element, when $i is 1, $array_id[$i] will print the second element, and so on.
Using .= you append text to a string. By the end of the loop, $sql will be a string with 3 queries ('UPDATE ... SET ...; UPDATE ... SET ...; UPDATE ... SET ...;').
Not sure if it's the best way, though. But you get the idea.
If yow want the status to be equal to the id, do this (single query):
UPDATE table SET status=id WHERE id IN (1,2,3);
Of course you can use some math, like:
UPDATE table SET status=(id+1)*2 WHERE id IN (1,2,3);
You didn't really explain why you need that, so
try1(childish): set status = id
"UPDATE table SET status = id"
It's a bad practice, and only you could understand what those numbers are. Plus if id is auto-increment, status will be auto-increment too, you will have a duplicate column. If status has only 3 values posible, you should not do this.
try2(basic): do 3 updates, or actually, do as many as you need with a for
if ($_POST){
$status = 1;
for ($i = 1; $i <= 3; $i++){
$sql ="UPDATE table
SET status = $status
WHERE id = $i;";
db()->query($sql);
$status++;
}
A better way bacause you have more control over the status. Of course the second try is irrelevant if you have only that 3 values. This one assumes you will change the $status variable inside the for loop, in concordance with the $i (id)
try3(mature): set one or 2 arrays with the concordance between id and status, so that either $arr[$id] will have the value of status and the key will be the id, or $arr1[$i] will have the value of id, and $arr2[$i] will have the value of status
the example will have only one array(also called map, because you map a value based on another value)
if ($_POST){
$status_array = array(1 => 1,2 => 2,3 => 3);
for ($i = 1; $i <= 3; $i++){
$sql ="UPDATE table
SET status = $status_array[$i]
WHERE id = $i;";
db()->query($sql);
}
Also, this works because the array is consistent. If you do not have an consistent array you should either work with 2 arrays, or try a foreach with key->value instead of for
I would suggest you to use the following code:
$theArray = array("1" => "1","2" => "2","3" => "3"); // The scheme is ID => Status
$errorMsg = false; // Our default assumption is that no error occured
foreach($theArray as $key => $value) {
$sql = "UPDATE table SET status =".$value." WHERE id = ".$key;
if(!db() -> query($sql)) { // Execute the query and check whether it failed
$errorMsg = "Query for ID ".$key." failed.";
break; // When the query failed we exit the loop and echo the error message
}
}
if($errorMsg) { // If an error occured (errorMsg is no longer false) we echo it here
echo $errorMsg;
}
Basically you do just create one array $theArray, which contains key => value pairs of the IDs and the statuses you want to give them. Afterwards, you loop through this array, execute the db() -> query() for each key => value pair and check whether it failed or not. If a query failed, you break the loop and output the error message.
Advantages:
Instead of using two arrays ($array_id, $array_status) I do use only one associative array $theArray. The advantage here is that you only have one instead of two arrays and that you can extend the number of rows you'd like to change without changing your code. Just extend the array.
The array $theArray does not need to be in a chronological order and you can give each ID independently of the other IDs a status.
You are executing the db() -> query($sql) in your code two times. This is not very efficient and redundant. Instead you can execute the command only once and immediately check whether it failed or not based on its return value inside the if().
The errorMsg I am creating in the code let you know which query failed so it gives you a more detailed information for debugging.
If you want to update multiple rows (in single query) using the INSERT syntax, you can do this:
REPLACE table(id,status) VALUES(1,1),(2,2),(3,3)
Notice that id must be Primary Key or Unique, otherwise the REPLACE will insert a new row.
Notice also that REPLACE isn't SQL standard, and works only in MySQL.

How do I get the first and last results from a query?

I am creating a pagination script and I need to get the first and last results in the database query so that I can determine what results appear when the user clicks a page to go to. This is the code that I have at the minute:
// my database connection is opened
// this gets all of the entries in the database
$q = mysql_query("SELECT * FROM my_table ORDER BY id ASC");
$count = mysql_num_rows($q);
// this is how many results I want to display
$max = 2;
// this determines how many pages there will be
$pages = round($count/$max,0);
// this is where I think my script goes wrong
// I want to get the last result of the first page
// or the first result of the previous page
// so the query can start where the last query left off
// I've tried a few different things to get this script to work
// but I think that I need to get the first or last result of the previous page
// but I don't know how to.
$get = $_GET['p'];
$pn = $_GET['pn'];
$pq = mysql_query("SELECT * FROM my_table ORDER BY id ASC LIMIT $max OFFSET $get");
// my query results appear
if(!$pn) {
$pn = 1;
}
echo "</table><br />
Page $pn of $pages<br />";
for($p = 1;$p<=$pages;$p++) {
echo "<a href='javascript:void(0);' onclick='nextPage($max, $p);' title='Page $p'>Page $p</a> ";
}
I think you have few problems there, but I try to tackle them for you. First, as comments say above, you are using code that it vulnerable to SQL injection. Take care of that - you might want to use PDO, which is as easy use as MySQL extension, and will save you from many trouble (like injection).
But to your code, lets go through it:
You should ask DB to get count of the rows, not using mysql function, it's far more effective, so use SELECT count(*) FROM mytable.
For $pages use ceil() as you want all rows to be printed, if you have $max 5 and have 11 rows, round will make $pages 2, where you actually want 3 (last page just contains that last 11th row)
in LIMIT you want to LIMIT row_count OFFSET offset. You can calculate offset from page number, so: $max = row_count but $offset = ($max * $page) - $max. In your code if $get is directly the page, it means you get $get'th row (Not sure though what happens in your JS nextpage. Bare in mind that not all use JavaScript.)
I have prepared simple example here which uses PDO, maybe that gives you idea how simple it's use PDO.
The selecting rows shows example how to put parameters in SQL, it would be perfectly safe in this case state, 'SELECT * FROM pseudorows LIMIT '.$start.','.$max by I wanted to make an example how easy it is (and then safe):
// DB config
$DB_NAME = 'test';
$DB_USER = 'test';
$DB_PASSWD = 'test';
// make connection
try {
$DB_CONN = new PDO("mysql:host=localhost;dbname=".$DB_NAME, $DB_USER, $DB_PASSWD);
$DB_CONN->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die($e);
}
// lets say user param 'p' is page, we cast it int, just to be safe
$page = (int) (isset($_GET['p'])?$_GET['p']:1);
// max rows in page
$max = 20;
// first select count of all rows in the table
$stmt = $DB_CONN->prepare('SELECT count(*) FROM pseudorows');
$stmt->execute();
if($value = $stmt->fetch()) {
// now we know how many pages we must print in pagination
// it's $value/$max = pages
$pages = ceil($value[0]/$max);
// now let's print this page results, we are on $page page
// we start from position max_rows_in_page * page_we_are_in - max_rows_in_page
// (as first page is 1 not 0, and rows in DB start from 0 when LIMITing)
$start = ($page * $max) - $max;
$stmt = $DB_CONN->prepare('SELECT * FROM pseudorows LIMIT :start,:max');
$stmt->bindParam(':start',$start,PDO::PARAM_INT);
$stmt->bindParam(':max', $max,PDO::PARAM_INT);
$stmt->execute();
// simply just print rows
echo '<table>';
while($row = $stmt->fetch()) {
echo '<tr><td>#'.$row['id'].'</td><td>'.$row['title'].'</td></tr>';
}
echo '</table>';
// let's show pagination
for($i=1;$i<=$pages;$i++) {
echo '[ '.$i.' ]';
}
}
mysql_fetch_array returns an associative array
Which means you can use reset and end to get the first and last results:
$pqa = mysql_fetch_array($pq);
$first = reset($pqa);
$last = end($pqa);
I don't see how you plan to use the actual results, just page numbers should be sufficient for pagination.
Still, hope it helps. And yes, upgrade to mysqli, so your code doesn't get obsolete.

How can I copy a database table to an array while accounting for skipped IDs?

I previously designed the website I'm working on so that I'd just query the database for the information I needed per-page, but after implementing a feature that required every cell from every table on every page (oh boy), I realized for optimization purposes I should combine it into a single large database query and throw each table into an array, thus cutting down on SQL calls.
The problem comes in where I want this array to include skipped IDs (primary key) in the database. I'll try and avoid having missing rows/IDs of course, but I won't be managing this data and I want the system to be smart enough to account for any problems like this.
My method starts off simple enough:
//Run query
$localityResult = mysql_query("SELECT id,name FROM localities");
$localityMax = mysql_fetch_array(mysql_query("SELECT max(id) FROM localities"));
$localityMax = $localityMax[0];
//Assign table to array
for ($i=1;$i<$localityMax+1;$i++)
{
$row = mysql_fetch_assoc($localityResult);
$localityData["id"][$i] = $row["id"];
$localityData["name"][$i] = $row["name"];
}
//Output
for ($i=1;$i<$localityMax+1;$i++)
{
echo $i.". ";
echo $localityData["id"][$i]." - ";
echo $localityData["name"][$i];
echo "<br />\n";
}
Two notes:
Yes, I should probably move that $localityMax check to a PHP loop.
I'm intentionally skipping the first array key.
The problem here is that any missed key in the database isn't accounted for, so it ends up outputting like this (sample table):
1 - Tok
2 - Juneau
3 - Anchorage
4 - Nashville
7 - Chattanooga
8 - Memphis
-
-
I want to write "Error" or NULL or something when the row isn't found, then continue on without interrupting things. I've found I can check if $i is less than $row[$i] to see if the row was skipped, but I'm not sure how to correct it at that point.
I can provide more information or a sample database dump if needed. I've just been stuck on this problem for hours and hours, nothing I've tried is working. I would really appreciate your assistance, and general feedback if I'm making any terrible mistakes. Thank you!
Edit: I've solved it! First, iterate through the array to set a NULL value or "Error" message. Then, in the assignations, set $i to $row["id"] right after the mysql_fetch_assoc() call. The full code looks like this:
//Run query
$localityResult = mysql_query("SELECT id,name FROM localities");
$localityMax = mysql_fetch_array(mysql_query("SELECT max(id) FROM localities"));
$localityMax = $localityMax[0];
//Reset
for ($i=1;$i<$localityMax+1;$i++)
{
$localityData["id"][$i] = NULL;
$localityData["name"][$i] = "Error";
}
//Assign table to array
for ($i=1;$i<$localityMax+1;$i++)
{
$row = mysql_fetch_assoc($localityResult);
$i = $row["id"];
$localityData["id"][$i] = $row["id"];
$localityData["name"][$i] = $row["name"];
}
//Output
for ($i=1;$i<$localityMax+1;$i++)
{
echo $i.". ";
echo $localityData["id"][$i]." - ";
echo $localityData["name"][$i];
echo "<br />\n";
}
Thanks for the help all!
Primary keys must be unique in MySQL, so you would get a maximum of one possible blank ID since MySQL would not allow duplicate data to be inserted.
If you were working with a column that is not a primary or unique key, your query would need to be the only thing that would change:
SELECT id, name FROM localities WHERE id != "";
or
SELECT id, name FROM localities WHERE NOT ISNULL(id);
EDIT: Created a new answer based on clarification from OP.
If you have a numeric sequence that you want to keep unbroken, and there may be missing rows from the database table, you can use the following (simple) code to give you what you need. Using the same method, your $i = ... could actually be set to the first ID in the sequence from the DB if you don't want to start at ID: 1.
$result = mysql_query('SELECT id, name FROM localities ORDER BY id');
$data = array();
while ($row = mysql_fetch_assoc($result)) {
$data[(int) $row['id']] = array(
'id' => $row['id'],
'name' => $row['name'],
);
}
// This saves a query to the database and a second for loop.
end($data); // move the internal pointer to the end of the array
$max = key($data); // fetch the key of the item the internal pointer is set to
for ($i = 1; $i < $max + 1; $i++) {
if (!isset($data[$i])) {
$data[$i] = array(
'id' => NULL,
'name' => 'Erorr: Missing',
);
}
echo "$i. {$data[$id]['id']} - {$data[$id]['name']}<br />\n";
}
After you've gotten your $localityResult, you could put all of the id's in an array, then before you echo $localityDataStuff, check to see
if(in_array($i, $your_locality_id_array)) {
// do your echoing
} else {
// echo your not found message
}
To make $your_locality_id_array:
$locality_id_array = array();
foreach($localityResult as $locality) {
$locality_id_array[] = $locality['id'];
}

Categories