0

I'm trying to make an autoincrement code with date and it will reset the code on the other day.

I have this migration.

public function up()
{
    Schema::create('records', function (Blueprint $table) {
        $table->id();
        //other codes here...
    });
}

enter image description here

Then, I add column that will auto increment 5 digits using

ALTER TABLE id ADD my_id AS RIGHT('00000' + CAST(id AS VARCHAR(5)) , 5) PERSISTED

enter image description here

I have this query on my controller

public function get_records(Request $request){
    $records = DB::table('records')->select(
        'records.my_id',
        //other codes here...
    )
    ->get();
    $recordlist = [];
    $currentMonth = Carbon::now()->format('mm');
    $currentDay = Carbon::now()->format('dd');
    foreach($records as $data):
        $data->CODE = 'Z' . $currentMonth . $currentDay . '-' .$data->my_id;
        $recordlist[] = $data;
    endforeach;
    return recordlist;

    // OUTPUT -->  CODE: 'Z0331-0001'
}

I would like my output like..

today is 03/31

CODE:
Z0331-00001
Z0331-00002
Z0331-00003
.....
Z0331-00010
Z0331-00011
Z0331-00012
.....
Z0331-00100
Z0331-00101
Z0331-00102

//The other day(04/01)..
CODE:
Z0401-00001
Z0401-00002
Z0401-00003
.....and so on

I just come up with this transaction.

I just add table,

ALTER TABLE records DROP COLUMN my_id

ALTER TABLE records ADD my_id VARCHAR(5) NOT NULL

Then, my query is..

$createdrecord = //last created record ex(CODE: 'Z0330-00265' created_at: '03/30(yesterday)' )
$today = Carbon::now();
foreach($records as $data){
    if($today != $createdrecord){
        $data->my_id = '00000';
    }else{
        $data->my_id += 1;
    }
}

I think this will OK, But my code doesn't go well..

Thank for your help.

gview
  • 14,876
  • 3
  • 46
  • 51
Newbee
  • 702
  • 1
  • 13
  • You want to write a custom sequence generator, ideally implemented as a stored procedure. See NoOorZ24 answer. Your table needs my_id and my_id_date. When you need a new ID, the sequence generator will get you one automatically. The only tricky code would be adding a sequence row for a day where one doesn't exist yet. – gview Mar 31 '23 at 08:23
  • You didn't state which DB you are using. If it's mysql I'm willing to share a skeleton of the code I implemented. The important thing with a sequence is that you lock the row. so you have concurrency but aren't prone to a race condition where the same sequence # might be allocated 2x or more. – gview Mar 31 '23 at 08:26
  • I'm using **SqlDbx**. [Here](https://i.imgur.com/Pghyp6r.png) is my sample table that i used with auto increment(some data is hidden) – Newbee Mar 31 '23 at 08:46
  • That is an admin tool, not an rdbms! What relational database are you using? Is it MySQL? Postgresql? Oracle? SQL Server...... – gview Mar 31 '23 at 08:54
  • Sorry, it's **MySql**. – Newbee Mar 31 '23 at 09:00

2 Answers2

2

You need a secondary table where you store counters. It has to have 2 columns:

  • date
  • counter

Every time you generate a record you have to check if there is a something in counters table for current date.

If it's empty - use ID = 1 and store it in counters table.

If it's not empty then use counter + 1 and increment counter

NoOorZ24
  • 2,914
  • 1
  • 14
  • 33
  • The problem is no data is added yet. So **code** start with 00000 and my counter is still NULL. – Newbee Mar 31 '23 at 08:50
  • It doesn't seem that you understand the idea here. This table he is talking about is a sequence table. The counter is used to keep track of the last id allocated. Everytime you need a new ID, you select the value from this table, and increment it by 1. You use the value you got to update your table's my_id. As I mentioned it's important that you lock the sequence row so it's impervious to a race condition. As he stated, if no row yet exists, you create one in your code, and set it's value to 1, and use 1 as your id. You will make the key of this table the date column. – gview Mar 31 '23 at 08:59
  • Thank you for explaining it. I think i got that idea. Sorry, i'm not quite understand english well. – Newbee Mar 31 '23 at 09:20
1

UPDATE In the process of trying to answer this question, we established that Author has MS Sql Server 2008, and not MySQL as originally stated.

Since MS Sql Server has Sequence objects, this idea could be implemented using a sequence naming scheme, where the sequences are named by Date.

  1. Search for Sequence named "SEQ_YYYY-MM-DD". The name of a sequence object can't start with a number, so you can use the SEQ_ prefix to get around this. For example:
select count(*) as count_of from sys.sequences where name = "SEQ_2023_04_04"
  1. If not found create one for the Day
create sequence SEQ_2023_04_04 START WITH 1 AS INT
  1. Now you can use the sequence to generate sequence numbers
SELECT NEXT VALUE FOR SEQ_2023_04_04

Note:

I leave the PHP details of these queries to you. For example, to know what the sequence name is for the current day in your code, you could use

$seq = 'SEQ_' . date('Y_m_d');

I omitted Schema here, as I don't know what you are actually doing, but schema names may have to be referenced in these queries as appropriate. You should make sure you understand the MS Sql server concepts of database vs. schema.


This is a MySQL implementation of the idea provided by NoOoZ24, so make sure to give him an upvote/credit.

First you need a sequence table:

CREATE table sequence (id DATE PRIMARY KEY, counter int unsigned DEFAULT 1);

Here is a MySQL stored procedure that will act as your sequence generator:

DELIMITER //
CREATE PROCEDURE `getSequenceCounter`(
    IN p_date DATE
)
BEGIN
  DECLARE v_id DATE;
  DECLARE v_counter INT DEFAULT 0;
  DECLARE v_errorMsg VARCHAR(256);

  DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING
  BEGIN
    ROLLBACK;
    RESIGNAL;
  END;

  START TRANSACTION;
  IF NOT EXISTS(
    SELECT *
    FROM sequence
    WHERE id = p_date
  ) THEN    
    INSERT INTO sequence (id) values (CURDATE());
  END IF;

  SELECT id, counter
  INTO v_id, v_counter
  FROM sequence
  WHERE id = p_date
  FOR UPDATE;

  UPDATE sequence SET counter = counter + 1
  WHERE id = v_id;
  COMMIT;
  SELECT v_counter as counter;
END;
//
DELIMITER ;

From a Command line, you can run this sproc using CALL:

call getSequenceCounter(CURDATE());

Looking at the code you should notice that it will create a sequence row for a date, should one not exist, and return you the initial value which will be 1.

call getSequenceCounter(CURDATE());
+---------+
| counter |
+---------+
|       1 |
+---------+

Run it again and you will see the next value, and so on.

Last I looked, there is not a way to call a mysql stored procedure from Eloquent, so you'll have to use DB::select or build the query. Both of those techniques are shown in answers to this question.

gview
  • 14,876
  • 3
  • 46
  • 51
  • Thank you. I'm still trying to figure it out using the sql procedure you given and try to execute it on my DB transaction. But i got some errors creating this ```table sequence``` , so i can't start with a table with sample [table sequence](https://i.imgur.com/0xtyjaa.png). – Newbee Apr 03 '23 at 02:54
  • Are you sure your database is MySQL and not SQL Server? From a SQL window of your tool, what do you get when you run SELECT @@version – gview Apr 03 '23 at 15:52
  • I'm very sorry, I give you the version of sql i've use in phpMyadmin.. It's **Microsoft SQL Server 2008 R2 (RTM)** .Thank you and sorry again. – Newbee Apr 03 '23 at 22:56
  • 1
    That version of SQL Server is end of life everyplace other than on Azure, and even with Azure, it's End of life in July this year. You should look at upgrading asap. – gview Apr 04 '23 at 20:19
  • Meaning, i cannot do/ any possible way to achieve the output in my problem using my current sql? – Newbee Apr 04 '23 at 21:42
  • I showed a method of steps to use a sql server sequence object. It is even easier, and doesn't require the simulation of a sequence, since sql server has sequences as objects. See the updates I made at the top of my answer. – gview Apr 04 '23 at 22:12