I'm trying to create a system for ordering and create a unique serial number to distinguish the order, it's working well until one time there is an order at same time (the difference is just in seconds, about 10 seconds) and then the unique serial number become same (the serial number is increment from the last serial number in db).
I'm creating the id based on some format and it have to be reseted per month so I can't use uniqid().
Do you guys have any idea about this? I read about some db locking but when I tried this solution "Can we lock tables while executing a query in Zend-Db" it's also didn't worked.
---EDIT---
The format is
projectnumber-year-number of order this months
the number of order this months is 4 digits started from 0001 until 9999
after 9999 it will start again from A001 ... A999 B001 ... ZZZZ
and this is the column
| order_id | varchar(17) | utf8_general_ci | NO | PRI |
I hope this make it more clear now :)
Thanks!
Primarily I'd look into using AUTO_INCREMENT primary key - see manual for details.
If that's not possible and you are using InnoDB you should be able to create the order in a Transaction. In your application you can then detect if there were duplicates and re-issue a new ID as needed. Using transaction will ensure that there is no residual data left in the database if your order creation fails.
EDIT based on the additional information:
I'd add an AUTO_INCREMENT primary key and use a separate "OrderName" column for the desired format. That should allow you to do the following, for example:
UPDATE orders o
JOIN (
SELECT
year(o2.dte) y,
month(o2.dte) m,
min(o2.Order_ID) minid
FROM orders o2 GROUP BY y,m) AS t ON (t.m=month(dte) AND t.y=year(dte))
SET o.OrderName=CONCAT('p-n-',year(o.dte),"-",o.Order_ID-t.minid);
id column is int PRIMARY KEY AUTO_INCREMENT and will ensure that the orders are always in correct order and will not require locking. In this example CONCAT will dictate your order number format. You can run this UPDATE in a trigger, if you wish, to ensure that the OrderName is immediately populated. Of course if you run this in a trigger, you don't need to repopulate the whole table.
Seem we must use transaction with Serializable locking. It will prevent read and write from other session until transaction complete.
Please view at here
http://en.wikipedia.org/wiki/Isolation_%28database_systems%29
http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
Related
I am currently using PHP & Laravel.
What I want to do is generating a unique and sequential string as code.
In this example, the code is for Purchase Orders.
What I want is something like this:
PO/000001
PO/000002
PO/000003
PO/000004
PO/000005
PO/000006
Database table schema:
create table `purchase_orders` (
`id` int unsigned not null auto_increment primary key,
`code` varchar(191) not null,
...
...
)
alter table `purchase_orders` add unique `purchase_orders_code_unique`(`code`)
This seems simple, I just need to grab the auto_increment ID, so the code corresponds with id, more or less like this:
id | code
1 | PO/000001
2 | PO/000002
3 | PO/000003
4 | PO/000004
5 | PO/000005
6 | PO/000006
The code I use: (uses Laravel's ORM syntax)
$count = PurchaseOrders::max('id'); // Equivalent to select MAX (`id`) from purchase_orders
$code = 'PO/' . sprintf('%08d', $count + 1);
In theory it works great, but in reality, often the 'collision' happens, where the id does not equal to the code it generated, sometimes the code is bigger/ahead than the id. For example, it often happens like this:
id | code
... | ...
199 | PO/000199
200 | PO/000200
201 | PO/000202
The next transaction will have the id of 202, and generated code is supposed to be PO/000202. It will trigger an Integrity constraint violation error, because PO/000202 is already used on id: 201.
I use DB transactions & commits heavily, and sometimes creating a purchase order takes a few moment, and also there are multiple users is creating purchase orders. I don't know how exactly it happened, but the collision occurs quite often, around 100 transactions or so.
Here's an occurrence from a live project:
As you can see, that the code is bigger than id. The next code to be inserted will be ...000205, and my client reported an error:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
'PRCV/JKT/G/2019/00000205' for key 'dlvr_payments_code_unique'
You can also see that the id and code is not equal at all
I also tried other code generation method before, instead of MAX('id') I use COUNT(*), but it seems worse than MAX('id').
So, the question is, what did I do wrong? How can I make it ensure a unique code every time so this won't happen again in the future?
I've been thinking of using Redis or some key-value database to store the counting, is that necessary?
Don't do it!
Don't depend on PK in order to achieve sequential number. It leaves you in various end streets.
I would recommend to store your last inserted Purchase Order ID in a separate table and keep updating it accordingly.
Along with maintaining a table, having it stored in cache (ie redis) can improve your application's performance.
Hope it helps!
I am trying to develop a system to assign room numbers to tenants of a hostel upon registration, using the auto increment feature of sql.
However, it automatically increases by one after every entry. Because the hostel accommodates four people in one room, I want to change this to 4, so that after every 4 entries I get only one id/room number.
How do I go about this? I am using php and sql. If the autoincrement feature is not possible can you please suggest another way to achieve this? Thanks.
You would need:
http://dev.mysql.com/doc/refman/5.1/en/replication-options-master.html#sysvar_auto_increment_increment
It works like this:
mysql> SET ##auto_increment_increment=4;
So when you insert 4 rows, the auto increment column will be:
4,8,12,16
as best of my knowledge you cannot change the steps of auto-increment field. I suggest add another field and write a trigger to update its value based on auto-increment field (auto-increment/4).
I don't think this is possible with autoincrement..
Maybe you can do something like this:
//Pseudo code
//First you get the count of the highest id, to see how many users are in the last room.
SELECT COUNT(*) FROM table WHERE id=(SELECT id FROM table ORDER BY id DESC LIMIT 1)
//If the result of the last query is >= 4 then insert the next customer with id +1
Don't use auto_increment for this - it can't handle a situation where multiple records will share the same number and although you can reset it manually (see below) it's also not designed for a situation where numbers may get reused in a random order.
You could just have a room_number field with one of the mysql integer types (e.g. tinyint, smallint, mediumint…) or you could separate your database into two tables, one for people (each of whom have an id) and a second to map those ids to rooms.
However you do it, you'd then write a select query to check which room numbers are available before you add the person's details to the database.
You may need to read up on relational databases if that doesn't sound very clear.
If you do need to reset the auto_increment (sometimes it's nice to do it if you've filled a database with test data which you're about to wipe, and you want the real "production" data to begin at 1) you can use:
ALTER TABLE [tablename] AUTO_INCREMENT = 1
https://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
I have 2 tables,
first table stores URLs
|link_id | link_url | <== schema url_table ::: Contains 2 million+ rows
and second table stores user_bookmarks
|user_id| link_id | is_bookmarked | <== schema for user_table ::: over 3.5 million+ rows
is_bookmarked stores 1 or 0, according to the link being bookmarked by the user or not.
Here is the problem,
When a new link is added, these are the steps followed
1) Check if url already exists in url_table , which means going through millions of rows
2)if does not exist add a new row in url_table and user_table
The Database(Mysql) is simply taking too much time, due to the enormous row-set,
Also, its a very simple php+Mysql app, with no search-assisted indexing programs whatsoever.
any suggestions to speed this up?
Why not remove the column user_bookmarks.is_bookmarked and use the sole existence of an entry with user_id and link_id as indicator that the link was bookmarked?
A new link has no entries in the user_bookmarks table, because nobody bookmarked it yet. When a user bookmarks a link, you add an entry. When the user removes the bookmark, you remove the row.
To check if a user bookmarked a link or not, simply SELECT count() FROM user_bookmarks WHERE user_id=? AND link_id=?. When you receive 1 row, it is bookmarked. When you receive 0 rows, it isn't.
Speeding up the insert-query when adding a new entry in the URL table could be accelerated with an appropriate index.
If you told us what your curent schema was (i.e. the create table statements including indexes) rather than just what your column names were then we might be able to make practical suggestions as to how to improve that.
There's certainly scope for improving the method of adding rows:
Assuming that the link_url can be larger than the 767 byte limit for an Innodb table (you didn't say what engine you are using), then change the id column to contain a md5 hash of the link_url with a unique index. Then when you want to add a record, go ahead and try to insert it using INSERT IGNORE ....
ID|RID|SID|Name
1| 1 | 1 |Alpha
2| 1 | 2 |Beta
3| 2 | 1 |Charlie
ID is auto-incrementing unique, not the problem here. RID is grouping sets of data together, I need a way to make the SID unique per RID 'group'. This structure is correct, I anticipate someone saying 'split it into multiple tables', that's not an option here (it's taxonomic classification).
As shown, under RID 1, the SID increments from 1 to 2, but when the RID changes to 2, the SID is 1.
I have the code to get the next value: SELECT IFNULL(MAX(SID),0)+1 AS NextVal FROM t WHERE RID=1, the question is how do I use that value when inserting a new record?
I can't simply run two queries as that can result in duplication, so somehow the table needs to be locked, ideally to write only. What would be the correct way to do this?
At first you should constraint your data to be exactly the way you want it to be, so put an unique combined index on (RID, SID).
For your problem you should start a transaction (BEGIN) and then put an exclusive lock onto the rows you need, which blocks all access to these rows for other connections (not the whole table, which is poor for performance!):
SELECT .... FOR UPDATE
This locks all selected rows exclusively. Further you should not use READ UNCOMMITTED as isolation level. you can view in the manual how to check the current isolation level and how to change this.
REPEATABLE READ is the default isolation level, which would be fine here.
Then insert your new query and commit (COMMIT) the transaction.
This should prohibit duplicates altogether since you created an unique index and it should also prohibit your scripts just failing with an error message that the unique check failed, but instead wait for other scripts to finish and insert the next row then.
Hi my qouestion is how to get the first number that is not used in specific database row. The number must be betwen 1 and 9999 and must be compared with all numbers in that specific database row, so if data in my database row starts with 5, i wont to be able to get the first number that is not used ...in this case the number 1. then when I create data with number 1.. the next number I need to get is 2 and...I'm using that to create profiles, and that number is the profile number, and ewery new profile must have the first unused number in data base. How to do that. I don't know where to start. So if someone can put me on the right path for solution of this problem? Thanks.
the edit
But, I dont need the auto increment i need to user to be able choosing this number on his own, first, this first number must bee suggested to the user by placeing it in the text form. And if the user select the number that is alredy in the database my program whil let the user know that he is trying to select the number that is allredy exist. So if you understand me ...I know the basics of mysql. The problem comes when the user deletes one profil then the deleted number can't be used eny more. For that i need the functio first free unused number.
New edit
I'l try to clear up some details...Frst this is the program for human resources and the user creates the dosies of workers... when user is creating the new dosie hee needs to select the dosie number for this worker, now I need to sugest to user the first unused number for the new dosie... the dosie number is not the dosie 'id'. Dosie number must be selected manualy by user or he can let the first free number to given to the new dosie... I think this whill clear some things
You are probably talking about auto-Increment primary key of table rows. Just insert the data, without specifying this "number" and the database will automatically set it to the proper (next free) value.
Do not reuse primary keys (eg you have 1,2,3,4,5 but then delete 3 - if you reuse 3 you will not know at any future point that 3 was some other record that was actually deleted).
This, btw, is very basic database knowledge. Read some introduction tutorials on MySQL or any other SQL relational database.
You are trying to use bad the database.
May be you can look this: Finding the next available id in MySQL
First create a table with values 1 to 9999. Then, run this query once:
delete from table where id IN (select id from profiles)
This way, you get IDs that are not in the profiles table. The first one can be shown to the user. On saving the record, make sure to delete that ID from this table.
If I understood you correctly, this is what you are looking for.
If you are limited to using values 1 through 9999 I would probably setup the process as follows:
Add another table with two columns (id_tracker).
Populate id_tracker with id's 1 through 9999 defaulting is_used to 0.
Update id_tracker.is_used to 1 based on the contents of your table.
Add a delete, insert triggers to your table to update the id_tracker as necesssary.
And select empty ID's as follows SELECT id FROM id_tracker WHERE is_used = 0 ORDER BY id LIMIT 1
Here's some SQL to get you started:
create table id_tracker
(id int not null, is_used tinyint default 0, primary key (id));
delimiter |
CREATE TRIGGER your_table_delete_trigger BEFORE DELETE ON your_table
FOR EACH ROW
BEGIN
UPDATE id_tracker SET is_used = 0 WHERE id = OLD.your_table_id;
END;
|
CREATE TRIGGER your_table_insert_trigger AFTER INSERT ON your_table
FOR EACH ROW
BEGIN
UPDATE id_tracker SET is_used = 1 WHERE id = NEW.your_table_id;
END;
|
delimiter ;
** NOTE: the above is for MySQL