My company has different type of invoices.
Example:
H00001/2013
.
.
.
H99999/2013
and
T00001/2013
.
.
.
T99999/2013
The problem is, the numbering is increasing for new year.
How can I make auto increment value reset for each new year?
This is my current code:
CREATE TABLE `invoices` (
`invoicenumber` mediumint unsigned NOT NULL auto_increment,
`invoicetype` enum('A','B') NOT NULL,
`date` date NOT NULL,
`client` varchar(100) NOT NULL,
PRIMARY KEY (invoicetype,invoicenumber)
) COMMENT='' ENGINE='MyISAM';
Hey if you are using any client application for database like MysqlWorkBench or MYSQL Query Browser
then you can do below steps to set AutoIncrement no -
Right click on Table and go to Alter Table
Select Options tab
Under that you can find Auto Increment label there you can reset the number.
You can reset auto increment value by using the ALTER TABLE statement. The syntax of the ALTER TABLE statement to reset auto increment value is as follows:
ALTER TABLE table_name AUTO_INCREMENT = VALUE;
EDITED:
If you don't want to run this query every year then you have other two option to do such thing as I am aware of this two.
Create cron job/windows scheduled job
As you are using MySql then there is Event Scheduler (Keep in mind this is added in MySql 5.1.6 not in previous versions of MySql)
In your DB table, you can reset the counter with the following code:
ALTER TABLE tablename AUTO_INCREMENT = 1
Perhaps you already found a solution and the answer I'm giving may not be useful, since it's been seven months. Returning to the topic, I noticed that you have a composed primary key with two columns (invoicetype,invoicenumber). So there can be no duplicates of pairs invoicetype,invoicenumber . If you reset the auto_increment every year there might be possible to have duplicate pairs like 'A',1 for two rows, one invoice from year 2013 and the other from year 2014. So you can eliminate that primary key to prevent the violation of the primary key constraint. You can instead define a primary key (any decent table has one) with an auto_incremented column to make every row unique. Then you can define an auto incrementation mechanism for the invoicenumber column (I will shall return to this issue). First I would define the invoice table like this:
CREATE TABLE `invoices` (
`id` int unsigned NOT NULL auto_increment,
`invoicenumber` mediumint unsigned NOT NULL,
`invoicetype` enum('A','B') NOT NULL,
`invoicedate` date NOT NULL, -- not recomended to use reserved words for column names like date
`client` varchar(100) NOT NULL,
PRIMARY KEY (id)
) COMMENT='' ENGINE='MyISAM';
Then I would define another table list_id :
CREATE TABLE `list_id` (
`id` int unsigned NOT NULL auto_increment,
`id_inc` int unsigned NOT NULL, -- number of invoice
`the_year` date NOT NULL, -- year corresponding to the number of invoice
PRIMARY KEY (id)
) COMMENT='' ENGINE='MyISAM';
The above table can be used to set the value of invoicenumber for the current row inserted in invoice table (1 if it is the first invoice of the year of invoicedate, the maximum value of id_inc (coresponding to the year of invoicedate) plus one, otherwise). The rows are completed using a trigger of type before insert for the invoice table. So, before I insert a new invoice, I have to determine the value of invoicenumber. It will be 1, if there are no records in table list_id with column the_year having the value equal to the year of the new invoice. In this case I can insert in table list_id a new record with the values (1,2014) (id_inc,year). It will be the maximum value of id_inc plus 1, if there are record(s) in table list_id with column the_year having the value equal to the year of the new invoice. In this case I can insert in table list_id a new record with the values (7,2014) (id_inc,year). The trigger looks like this:
CREATE TRIGGER `increment_or_reset_new_year`
BEFORE INSERT ON `invoices`
FOR EACH ROW
thisTrigger : begin
declare new_id_year int(11);
declare nr_invoices_year int(11);
declare new_invoice_begin int(11);
declare current_year_row int(11);
set current_year_row = year(new.invoice_date);
set nr_invoices_year = get_nr_invoices_year(current_year_row);
if(get_nr_invoices_year(current_year_row) < 1) then
set new.invoicenumber = 1;
insert into list_id(id_inc,the_year) values (1,current_year_row);
leave thisTrigger;
end if;
if(get_nr_invoices_year(current_year_row) >= 1) then
set new.invoicenumber = get_max_id(year(new.invoice_date)) + 1;
set new_id_year = get_max_id(year(new.invoice_date)) + 1;
insert into list_id(id_inc,the_year) values(new_id_year,year(new.invoice_date));
end if;
end;
There are 2 functions in the trigger. The first one determines the number of rows from the list_id table having the_year equal with the current invoice year (given as parameter):
create function get_nr_invoices_year(invoice_year int) returns int
begin
declare nr_invoices_year int(11);
select count(*) into nr_invoices_year from lista_id where the_year = invoice_year;
return nr_invoices_year;
end;
The second one determines the maximum value of id_inc from table list_id which has the coresponding value of the_year equal with the year of current invoice (given as parameter):
create function get_max_id(year_invoice int) returns int
begin
declare max_id_year int(11);
select max(id_inc) into max_id_year from invoices.lista_id where the_year =year_invoice;
return max_id_year;
end;
So I can have one trigger, two functions and one table which controls the incrementation of invoicenumber.
Hope this helps! It worked for me!
Related
im using mysql to make a table
CREATE TABLE school.student(
Std_id INT NOT NULL AUTO_INCREMENT,
Std_name CHAR(40) NOT NULL,
Std_Birth DATE,
Std_Group CHAR(2) check(Std_Group in ('G1', 'G2','G3','G4'))
);
and im trying to make the auto incrment start from 1000 and incrment by 2 (1000,1002,1004,1006.....) while using CREATE
You may try this solution.
CREATE TABLE school.student(
Std_id INT NOT NULL AUTO_INCREMENT,
Std_name CHAR(40) NOT NULL,
Std_Birth DATE,
Std_Group CHAR(2) check(Std_Group in ('G1', 'G2','G3','G4'))
) AUTO_INCREMENT = 1000;
set ##auto_increment_increment=2;
while, you set the value of ##auto_increment_increment it will
effect all over the database. Because, It's Global variable for
MySQL.
For setting starting value from 1000 you may need to set
AUTO_INCREMENT at the end of the Create Table syntax.
You can do it as follows :
INSERT INTO _students
(Std_id, Std_name, Std_Birth, Std_Group)
select case when count(Std_id) >= 1 then max(Std_id) + 2 else 1000 end as Std_id, 'test', '2022-10-10', 'G1'
from _students;
select case when count(Std_id) >= 1 then max(Std_id) + 2 else 1000 end : this will check if there are any records in your table, if not it will insert first with id 1000
Create table #autoincre
(
Std_id int not null Primary key identity (1000,2),
Std_name char(40) Not null,
Std_Birth date,
Std_Group char(2) check(Std_group in ('G1','G2','G3','G4'))
)
Drop table #autoincre
insert into #autoincre values('Ajay','2022-07-10','G1')
select * from #autoincre
You can start the auto_increment for a given table at 1000 with a table option:
CREATE TABLE school.student(
...
) AUTO_INCREMENT=1000;
Note that the next id generated will be one greater than the table option is set to, so it will generate value 1001. If you want it to include the value 1000, set the table option AUTO_INCREMENT=999.
Your other requirement is more difficult.
https://dev.mysql.com/doc/refman/8.0/en/replication-options-source.html#sysvar_auto_increment_increment says:
It is not possible to restrict the effects of these two variables to a single table; these variables control the behavior of all AUTO_INCREMENT columns in all tables on the MySQL server.
In other words, if you set auto_increment_increment=2, this will apply to all tables with an auto-increment column. It is not a per-table option.
You could set auto_increment_increment=2 as a session variable only before inserting into the student table, then set it back to the default before inserting to another table. That sounds like it will be error-prone, because you could forget to change the session variable.
I am using Mysql 8.0
I have 2 tables given below:
Table 1:
I_VendorID INT NOT NULL PRIMARY KEY,
V_Code VARCHAR(50) NULL,
V_Name VARCHAR(250) NULL,
V_Address VARCHAR(500) NULL,
N_Mobile NUMERIC(10, 0) NULL,
N_Phone NUMERIC(11, 0) NULL,
V_Email VARCHAR(100) NULL,
Table 2:
I_POID INT NOT NULL PRIMARY KEY,
I_VendorID INT NULL,
V_PODetails VARCHAR(50) NULL
I wish to know if there is any way, either through query code or through mysql workbench that whatever value is in table 1 under I_VendorID is automatically copied to I_VendorID in table 2.
I will always be taking the values from the user for the 2 tables at the same time. Also declared I_VendorID in table 2 as a foreign key. I thought, rather than passing values manually for I_VendorID from table 2 everytime, I could just copy the value from table 1. Is there any query or method that would automatically ensure this?
You can use mysql triggers to make sure that for each row inserted in your first table, another row is inserted in the second table
MySql documentation on triggers
CREATE TRIGGER ins_to_table_2 BEFORE INSERT ON table_1
FOR EACH ROW
BEGIN
insert into table_2('I_VendorID') values(New.I_VendorID)
END;//
I have not executed the above yet, but it's something very similar.
Truth be told though, since you will eventually add a row in the second table, you can insert it when needed and query with a left join instead of inner join so that you can return the values from the first table, even if the second one has no values yet.
so I have this two tables which both have some trigger, both are AFTER INSERT; I dont exactly know how to call it but I hope the title is not so inaccurate. :)
I'm currently new to triggers in general so bear with me on this one please.
What happens is that for every new episode entry I need to updated the count of episodes per season. And when a new entry in the season table a new episode shall be created (a trailer).
I'm currently using MySQL 5.7.17; This triggers work in DB2 but in MySQL I just couldnt get them to work.
I just keep getting the error that says that I can't update the table from which the statement was called:
Can't update table 'seasons' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
If you can help me, or not, anyways any ideas THANKS IN ADVANCE!
CREATE TABLE episodes (
episodeId INTEGER NOT NULL AUTO_INCREMENT,
seasonId INTEGER NOT NULL,
number INTEGER NOT NULL,
date DATE NOT NULL,
title VARCHAR(256) NOT NULL,
summary TEXT,
PRIMARY KEY(episodeId)
);
CREATE TABLE seasons (
seasonId INTEGER NOT NULL AUTO_INCREMENT,
startDate DATE NOT NULL,
name VARCHAR(128) DEFAULT 'Unknown season',
numberOfEpisodes SMALLINT NOT NULL DEFAULT 0,
PRIMARY KEY(seasonId)
);
/* TRIGGERS */
/* trigger to increment the numberOfEpisodes in seasons */
CREATE TRIGGER newEpisode
AFTER INSERT ON episodes
FOR EACH ROW
BEGIN -- I also tried without the Begin-End statement
UPDATE seasons SET numberOfEpisodes = numberOfEpisodes + 1
WHERE seasons.seasonId = NEW.seasonId;
END;
;
/* Each season has a trailer as episode 0 */
CREATE TRIGGER newSeason
AFTER INSERT ON seasons
FOR EACH ROW
BEGIN -- I also tried without the Begin-End statement
INSERT INTO episodes (seasonId, number, date, title, summary)
VALUES (NEW.seasonId, 0, CURRENT_TIMESTAMP, 'Trailer', 'Episode 0 = Trialer');
END;
;
We are using a table which has schema like following:-
CREATE TABLE `user_subscription` (
`ID` varchar(40) NOT NULL,
`COL1` varchar(40) NOT NULL,
`COL2` varchar(30) NOT NULL,
`COL3` datetime NOT NULL,
`COL4` datetime NOT NULL,
`ARCHIVE` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
)
Now we wanted to do partition on column ARCHIVE. ARCHIVE can have only 2 values 0 or 1 and so 2 partitions.
Actually in our case, we are using partitioning as a Archival process. To do partition, we need to make ARCHIVE column as a part of primary key. But the problem here is that 2 rows can have same ID with different ARCHIVE column value. Actually thats not the main problem for us as 2 rows will be in different partitions. Problem is when we will update the archive column value of one of them to other to move one of the row to archive partition, then it will not allow us to update the entry giving "Duplicate Error".
Can somebody help in this regard?
Unfortunately,
A UNIQUE INDEX (or a PRIMARY KEY) must include all columns in the table's partitioning function
and since MySQL does not support check constraints either, the only ugly workaround I can think of is enforcing the uniqueness manually though triggers:
CREATE TABLE t (
id INT NOT NULL,
archived TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id, archived), -- required by MySQL limitation on partitioning
)
PARTITION BY LIST(archived) (
PARTITION pActive VALUES IN (0),
PARTITION pArchived VALUES IN (1)
);
CREATE TRIGGER tInsert
BEFORE INSERT ON t FOR EACH ROW
CALL checkUnique(NEW.id);
CREATE TRIGGER tUpdate
BEFORE UPDATE ON t FOR EACH ROW
CALL checkUnique(NEW.id);
DELIMITER //
CREATE PROCEDURE checkUnique(pId INT)
BEGIN
DECLARE flag INT;
DECLARE message VARCHAR(50);
SELECT id INTO flag FROM t WHERE id = pId;
IF flag IS NOT NULL THEN
-- the below tries to mimic the error raised
-- by a regular UNIQUE constraint violation
SET message = CONCAT("Duplicate entry '", pId, "'");
SIGNAL SQLSTATE "23000" SET
MYSQL_ERRNO = 1062,
MESSAGE_TEXT = message,
COLUMN_NAME = "id";
END IF;
END //
(fiddle)
MySQL's limitations on partitioning being such a downer (in particular its lack of support for foreign keys), I would advise against using it altogether until the table grows so large that it becomes an actual concern.
I'm trying to export data from a multivalue database (Unidata) into MySQL. Lets say my source data was a person's ID number, their first name and all the states they've lived in. The states field is a multi value field and I'm exporting them so that the different values within that field are seperated by a ~. A sample extract looks like:
"1234","Sally","NY~NJ~CT"
"1235","Dave","ME~MA~FL"
"3245","Fred","UT~CA"
"2344","Sue","OR"
I've loaded this data into a staging table
Table:staging
Column 1: personId
Column 2: name
Column 3: states
What I want to do is split this data out into two tables using a procedure: a persons table and a states table. A person can have many entries in the states table:
Table 1: persons
Column 1: id
Column 2: name
Table 2: states
Column 1: personId
Column 2: state
My procedure takes the data from the staging table and dumps it over to table 1 just fine. However, i'm a little lost how how to split the data up and send it to table 2. Sally would need to have three entries in the states table (NY, NJ, CT), Dave would have 3, Fred would have 2 and Sue would have1 (OR). Any ideas on how to accomplish this?
try something like this : http://pastie.org/1213943
-- TABLES
drop table if exists staging;
create table staging
(
person_id int unsigned not null primary key,
name varchar(255) not null,
states_csv varchar(1024)
)
engine=innodb;
drop table if exists persons;
create table persons
(
person_id int unsigned not null primary key,
name varchar(255) not null
)
engine=innodb;
drop table if exists states;
create table states
(
state_id tinyint unsigned not null auto_increment primary key, -- i want a nice new integer based PK
state_code varchar(3) not null unique, -- original state code from staging
name varchar(255) null
)
engine=innodb;
/*
you might want to make the person_states primary key (person_id, state_id) depending on
your queries as this is currently optimised for queries like - select all the people from NY
*/
drop table if exists person_states;
create table person_states
(
state_id tinyint unsigned not null,
person_id int unsigned not null,
primary key(state_id, person_id),
key (person_id)
)
engine=innodb;
-- STORED PROCEDURES
drop procedure if exists load_staging_data;
delimiter #
create procedure load_staging_data()
proc_main:begin
truncate table staging;
-- assume this is done by load data infile...
set autocommit = 0;
insert into staging values
(1234,'Sally','NY~NJ~CT'),
(1235,'Dave','ME~MA~FL'),
(3245,'Fred','UT~CA'),
(2344,'Sue','OR'),
(5555,'f00','OR~NY');
commit;
end proc_main #
delimiter ;
drop procedure if exists cleanse_map_staging_data;
delimiter #
create procedure cleanse_map_staging_data()
proc_main:begin
declare v_cursor_done tinyint unsigned default 0;
-- watch out for variable names that have the same names as fields !!
declare v_person_id int unsigned;
declare v_states_csv varchar(1024);
declare v_state_code varchar(3);
declare v_state_id tinyint unsigned;
declare v_states_done tinyint unsigned;
declare v_states_idx int unsigned;
declare v_staging_cur cursor for select person_id, states_csv from staging order by person_id;
declare continue handler for not found set v_cursor_done = 1;
-- do the person data
set autocommit = 0;
insert ignore into persons (person_id, name)
select person_id, name from staging order by person_id;
commit;
-- ok now we have to use the cursor !!
set autocommit = 0;
open v_staging_cur;
repeat
fetch v_staging_cur into v_person_id, v_states_csv;
-- clean up the data (for example)
set v_states_csv = upper(trim(v_states_csv));
-- split the out the v_states_csv and insert
set v_states_done = 0;
set v_states_idx = 1;
while not v_states_done do
set v_state_code = substring(v_states_csv, v_states_idx,
if(locate('~', v_states_csv, v_states_idx) > 0,
locate('~', v_states_csv, v_states_idx) - v_states_idx,
length(v_states_csv)));
set v_state_code = trim(v_state_code);
if length(v_state_code) > 0 then
set v_states_idx = v_states_idx + length(v_state_code) + 1;
-- add the state if it doesnt already exist
insert ignore into states (state_code) values (v_state_code);
select state_id into v_state_id from states where state_code = v_state_code;
-- add the person state
insert ignore into person_states (state_id, person_id) values (v_state_id, v_person_id);
else
set v_states_done = 1;
end if;
end while;
until v_cursor_done end repeat;
close v_staging_cur;
commit;
end proc_main #
delimiter ;
-- TESTING
call load_staging_data();
select * from staging;
call cleanse_map_staging_data();
select * from states order by state_id;
select * from persons order by person_id;
select * from person_states order by state_id, person_id;