I want to reset number daily,
Table :
CREATE TABLE tickets(
ticketId varchar(10),
moviesname text,
date DATE,
seatnumber INT(5),
PRIMARY KEY(ticketId));
is there anything SQL Syntax to reset the id daily ?
I see 2 possible approaches:
1. Use myisam table's special auto_increment feature:
For MyISAM tables, you can specify AUTO_INCREMENT on a secondary
column in a multiple-column index. In this case, the generated value
for the AUTO_INCREMENT column is calculated as
MAX(auto_increment_column) + 1 WHERE prefix=given-prefix.
Make sure that tickets table is myisam.
tickeid column must be auto_increment
change the primary key to be date, tickeid. The order of the columns is paramount!!!!
2. Spice-up your current max() query to determine the next value
Change the primary key to be date, tickeid. Order of the fields is not important. You cannot leave ticketid to be the pk alone, since you want to restart the numbering daily, which would lead to duplicate index value error message.
Use the following query to get the next ticketid:
select coalesce(max(ticketid),1) from tickets where date=curdate()
The where criteria ensures that every day the ticketid values would restart. The coalesce() is there to assign the number 1 as the first id every day, since max() returns null if there are no records to satisfy the where criteria.
However, the risk with this solution is that multiple statements executed at the same time may yield the same id. Either you handle this situation with proper error handling within your application code (e.g. attempt to insert again), or you serialise the inserts into the tickets table by using explicit locks.
Related
I was developing a database in SQL Server where I was using an identity column as a seed for a primary key field. The intention was to reset the identity to 1 at the beginning of every year. This would allow us to create a PK of the Year - Identity Column.
Create Table Issues (
IssueID AS RIGHT(CONVERT(VARCHAR, Year(getdate()), 4),2) + '-' + RIGHT(REPLICATE('0', 2) +
CONVERT(VARCHAR, RecordID),3) NOT NULL PRIMARY KEY,
RecordID int Identity (1,1),.........)
The result would be
IssueID RecordID
20-001 1
20-002 2
20-003 3
21-001 1
etc....
Now I've been told we are going to use a MySQL database instead.
Can an Auto-Increment field in MySQL contain duplicate values like it can in SQL Server?
If Not, how can I do what I need to do in MySQL?
In MySQL, you can't use the default auto-increment feature for what you describe, a incrementing value that starts over per year.
This was a feature of the MyISAM storage engine years ago. An auto-increment that was the second column of a multi-column primary key would start counting from one for each distinct value in the first column of the PK. See the example under "MyISAM Notes" on this page: https://dev.mysql.com/doc/refman/8.0/en/example-auto-increment.html
But it's considered not a good idea to use MyISAM because it does not support ACID. In general, I would find another way of solving this task. I would not use MyISAM.
In InnoDB, there's no way the table will generate a value that is a duplicate of a value currently in the table, or even a value less than the maximum value previously generated for that table. In other words, there's no way to "fill in the gaps" using auto-increment.
You can use ALTER TABLE mytable AUTO_INCREMENT=1 to reset the counter, but the value you set it will automatically advance to the max value currently in the table + 1.
So you'll have to generate it using either another table, or else something other than the MySQL database. For example, I've seen some people use memcached, which supports an atomic "increment and return counter" operation.
Another thing to consider: If you need a row counter per year, this is actually different from using MySQL's auto-increment feature. It's not easy to use the latter as a row counter. Besides, what happens if you roll back a transaction or delete a row? You'd end up with non-consecutive RecordId values, with unexplained "gaps." It's also a fact about the auto-increment feature that it guarantees that subsequent id's will be greater, but it does not guarantee to generate all values consecutively. So you'll get gaps eventually anyway.
In MySQL a table can have only one auto_increment column and this column must be a part of the primary key. See details here.
Technical workaround for your task would be creating of a table with a single auto_increment column, and you can obtain auto_increment value by inserting a record into this table and immediately calling standard MySQL function last_inser_id(). When time comes you should truncate the table - in this case the auto_increment count will be reset.
I have a lists table that has an order field.
When I insert a new record, is it possible to find the order of the previous row and increment the new row?
Or should I go about it myself in PHP by doing an OrderBy('order') query and getting the max() value of that?
When you declare a table with MySQL you can use an auto-increment id so you won't have to deal about its incrementation:
CREATE TABLE people (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
);
As explained in the documentation,
An integer or floating-point column can have the additional attribute
AUTO_INCREMENT. When you insert a value of NULL (recommended) or 0
into an indexed AUTO_INCREMENT column, the column is set to the next
sequence value. Typically this is value+1, where value is the largest
value for the column currently in the table. AUTO_INCREMENT sequences
begin with 1.
I suggest you to ommit the field completly when inserting new records.
You can then retrieve the last id inserted with LAST_INSERT_ID() SQL function (or the mysqli_insert_id function of PHP languagefor example).
But since it's not what you wanted, probably because of one of the reasons quoted from MarioZ's comment:
If you are already using auto-increment for the ID you can use it for
the order (that can be one reason). For auto-increment the column
must be set as primary and unique, can't be repeated values. The auto-increment is from the number in the record, if you inserted 10
rows and you delete 2, the next insert with auto-increment will be
11(if the last now is 8 you'd want it to be 9). Those are posible
reasons not to use it for what #Notflip wants :P
... You'll have to use PHP, with LOCK TABLE and UNLOCK TABLE SQL instructions before and after the retrieving of the last order then the updating of the new order, to avoid having simultaneous records with the same "order".
I am creating a table which will store around 100million rows in MySQL 5.6 using InnoDB storage engine. This table will have a foreign key that will link to another table with around 5 million rows.
Current Table Structure:
`pid`: [Foreign key from another table]
`price`: [decimal(9,2)]
`date`: [date field]
and every pid should have only one record for a date
What is the best way to create indexes on this table?
Option #1: Create Primary index on two fields pid and date
Option #2: Add another column id with AUTO_INCREMENT and primary index and create a unique index on column pid and date
Or any other option?
Only select query i will be using on this table is:
SELECT pid,price,date FROM table WHERE pid = 123
Based on what you said (100M; the only query is...; InnoDB; etc):
PRIMARY KEY(pid, date);
and no other indexes
Some notes:
Since it is InnoDB, all the rest of the fields are "clustered" with the PK, so a lookup by pid is acts as if price were part of the PK. Also WHERE pid=123 ORDER BY date would be very efficient.
No need for INDEX(pid, date, price)
Adding an AUTO_INCREMENT gains nothing (except a hint of ordering). If you needed ordering, then an index starting with date might be best.
Extra indexes slow down inserts. Especially UNIQUE ones.
Either method is fine. I prefer having synthetic primary keys (that is, the auto-incremented version with the additional unique index). I find that this is useful for several reasons:
You can have a foreign key relationship to the table.
You have an indicator of the order of insertion.
You can change requirements, so if some pids allows two values per day or only one per week, then the table can support them.
That said, there is additional overhead for such a column. This overhead adds space and a small amount of time when you are accessing the data. You have a pretty large table, so you might want to avoid this additional effort.
I would try with an index that attempts to cover the query, in the hope that MySQL has to access to the index only in order to get the result set.
ALTER TABLE `table` ADD INDEX `pid_date_price` (`pid` , `date`, `price`);
or
ALTER TABLE `table` ADD INDEX `pid_price_date` (`pid` , `price`, `date`);
Choose the first one if you think you may need to select applying conditions over pid and date in the future, or the second one if you think the conditions will be most probable over pid and price.
This way, the index has all the data the query needs (pid, price and date) and its indexing on the right column (pid)
By the way, always use EXPLAIN to see if the query planner will really use the whole index (take a look at the key and keylen outputs)
I am facing a problem in my application. I have a table that one field name is registration_no. Before inserting a new record i increment registration_no field by 1 and then insert that incremented registration_no in that table. the problem is when some user concurrently insert data some registration_no value has been same. how can i prevent this.
You want to use a sequence.
Two caveats:
The AUTO_INCREMENT feature described in the article is non-standard and may give portability issues when moving to a different database.
If an INSERT is aborted, a number from the sequence is consumed still, so you may end up with holes in the sequence. If that is unacceptable, use an autogenerated sequence for the primary (surrogate) key, and add a separate map from that key to the "official" sequence number, enforcing uniqueness in the index of that table.
The alternative is to enforce UNIQUEness in the database, use an appropriate TRANSACTION ISOLATION LEVEL and add application logic to handle failure to INSERT.
You could have the database set the registration_no for you, and not do this in code. You can get the registration_no in the result of an insert statement and this will solve your concurrency problem.
alter table myTable modify column registration_no int auto_increment
The result of your query will be the index of the record. IF registration_no is not the index your will need to query the auto generated registration_no based on the returned index id.
I have a unique PK 'id' which is set to auto_increment. I have a second field called 'label' which is an alphanumeric field (e.g. W1000) that is intended to increment on each insert using PHP logic.
The 'label' field may have one of many alpha prefixes, followed by the incrementing number. Each prefix should increment independently. For example, the table may have W1000 and F1123. The next W would be W1001, and the next F would be F1124.
The current method (PHP select maximum label, insert maximum label + 1) creates a race condition, and occasionally I get a duplicate 'label'. I need to resolve these duplicated 'labels' and ensure that this field is unique. I am willing to split the prefix and the numeric into two fields if it helps.
What is the best way to accomplish this?
One approach to avoid generating duplicate label values is to use a MyISAM table to generate unique sequence numbers. MyISAM supports the behavior of AUTO_INCREMENT you need.
See the "MyISAM Notes" section in MySQL Reference 3.6.9. Using AUTO_INCREMENT
For this approach, you would create a separate MyISAM table; the purpose of this table is to generate unique sequence numbers:e.g.
CREATE TABLE foo
( prefix VARHCAR(1) NOT NULL
, num INT UNSIGNED AUTO_INCREMENT
, PRIMARY KEY (prefix, num)
) Engine=MyISAM
Assuming that the label prefix is one character, and the remainder is a numeric:
INSERT INTO foo (prefix, num)
SELECT SUBSTR(t.label,1,1) AS prefix
, MAX(SUBSTR(t.label,2,8) AS num
FROM mytable
GROUP BY SUBSTR(t.label,1,1)
Go get a new sequence number, insert a row to the new table, providing a value for prefix, and a NULL for the num column, and retrieve the value inserted for the num column:
INSERT INTO foo (prefix,num) VALUES ('W',NULL);
SELECT LAST_INSERT_ID();
You can use that to construct the value to be used for the label column in your original table.
Note that it's only the MyISAM engine that has the behavior you want (separately incrementing AUTO_INCREMENT sequences for each prefix.) Your original table can be whatever engine.
This approach avoids the race condition, but does introduce a concurrency bottleneck, due to the exclusive locks taken on the MyISAM table for the insert.
Another way to avoid the race condition is to obtain an exclusive lock on the table, then do a SELECT MAX(), then do an insert, then release the lock. But that approach introduces even more of a concurrency bottleneck, serializing access to a single resource.
If your question is about identifying the existing duplicate label values, then this query gets you rows that have a "duplicate" label. (This is picking out just one row, for each duplicated label.)
SELECT t.label
, MAX(t.id)
FROM mytable t
GROUP BY t.label
HAVING COUNT(1) > 1
To update the labels to be unique, you would need to generate a new label for those rows.
Go get that done in a single SQL statement is a bit tricky. I tried to come up with a statement, but it was broken, and I don't have the time to fix it.