ElPiter ElPiter - 5 months ago 23
SQL Question

Symfony2, Doctrine and MySQL: how to generate invoice number

Using Symfony2.0, Doctrine and MySQL, I need to automatically generate correlative invoice numbers according to the next format:

year/autoincrementable

For example, invoices for year 2012:

2012/00000001
2012/00000002
2012/00000003


etc...

That invoice number will be stored using an
invoice_number
field in my Purchase table in my MySQL database.

The question is, how can I query the database to return the last autoincrementable number of certain year?

In other words (pseudocode):

function new_invoice(){
$year = today.year;
select last invoice_number where year = $year;
$new_invoice_number = increment invoice_number;
store $year . "/" . $new_invoice_number;
}


How can I do that?

Answer

First of all, neither symfony2 nor doctrine2 are part of your problem and your solution. Second, you may want to think about storing a / in your invoice_number, mainly because you need to make the field a varchar for this which performs worse than an integer on stuff like indexing and quering for items.

That said, what you want can be achieved in many ways, depending on your needs:

Select last entry, increment by one, save

This is a solution which will only work if either you have not that much writes to your purchase table or these writes are done by one process (e.g. a batch) where you can wait for one item to be written and if this succeeds write the next one.

Simply select the last entry from the database, get the invoice_number, increment by one, save. If the year is different, start with one.

Of course, if you have many writes per second, you get into trouble because the moment you write a row, two others may have read the old last invoice_number, increment it by one and are trying to store it (resulting in errors because they need to be unique).

Calculate dynamically on read

This solution works well if you don't need the invoice_number that often. You could have a normal auto_increment on an idea. For each year, you store the last id of the last year in some helper table. When you read a row, you can calculate the id by getting the year, the last id of the last year and subtract that from the auto_increment.

The overhead is of course that you need to calculate the invoice everytime. Depending on how you actual implement it and what the database load is this can be anywhere from not noticeable to very slow.

Let invoice_number empty, than calculate it

This can be done in many different ways and is a very neat solution if you don't want to fiddle with transactions and the like, catching failed writes and so on. The idea is to first store the data with an empty invoice_number, then when the data is saved calculate the invoice_number and update the dataset.

You could combine this with the previous idea, calculating the invoice_number like described above and then update the dataset with this number instead of recalculating it everytime. You could also have a cronjob or something looking for rows with empty invoice_number and something like a counter in a helper table which only gets used by this cronjob. He takes the counter, increments it, uses this is the new invoice for the dataset and saves both the counter and the dataset.

Try, repeat on error

This is a ugly solution, but it works and may be ok in some cases. You select the last invoice_number, increment it by one, save the current dataset. If it fails, you repeat the process. You do this till you success with saving. You need to make sure to do the select on the actual database (doctrine2 may cache results, meaning you get the old dataset all the time, increment the same number and thereby hit the same error over and over) and if you have some master/slave setup not work on the slave as it may not be up-to-date.

I don't like this solution but, as said, it is something worth considering.

One table per year

It would be possible to save the data in one table per year. This way you could use the auto_increment. Of course foreign keys are getting messed up and selects over many tables are not that easy than if you have one table. You could of course introduce a view which combines all this tables.

Use a stored procedure

I was never a fan of stored procedures, mainly because it takes logic from the application to the database, making it harder to grasp what's happening ("Where is this value coming from?"). But you could have some stored procedure which calculates the invoice_number and stores it with the other values on a INSERT INTO

Just use the auto_increment

You save yourself a lot of troube if you just use the auto_increment. You can always save the year in another column and display the two together. Of course, if your requirement is from the product owner, this may not be possible. But if you have any way of doing this, try it!

...

There may be many more. These ones are just from the top of my head.

Comments