Auto add 0 to the int field - mysql - mysql

I'm building some application, which is connected to mysql database.
In mysql in let say table test I have an Integer field called myId.
With my first insert to the myId value "1" is added, with my second insert i'm adding value "20".
Now my question is: is there any possibility, that to the first value "1" after second insert will be auto added "0".
So i will have:
|myId|
10
20
And if a add "300", so to the two first inserts will be added next 0 ?
|myId|
100
200
300

Just do it when you select data
set #max:=(select max(length(myId)) from your_table);
SELECT myId * power(10, (#max - length(myId))) from table;

unverified:
delimiter |
CREATE TRIGGER upd_entries AFTER INSERT ON your_table
FOR EACH ROW
BEGIN
update your_table
set myId = myId * power(10, ((select * from (select max(length(myId)) from your_table)) - length(myId)));
END;
|
delimiter ;

Related

Writing stored procedure which flags duplicate values in a comma separated field in MySQL

I have a database table like this sample:
ID THINGS HAS_DUPLICATES
1 AAA, BBB, AAA NULL
2 CCC, DDD NULL
I am trying to write a stored procedure to flag duplicate values in THINGS field.
After calling the procedure the table will become like this:
ID THINGS HAS_DUPLICATES
1 AAA, BBB, AAA YES
2 CCC, DDD NO
Please be informed that I am trying to resolve it using only SQL and without normalizing my database. I am also aware of other approaches like writing PHP code.
Schema:
DROP TABLE IF EXISTS evilThings; -- orig table with dupes
CREATE TABLE evilThings
( ID INT AUTO_INCREMENT PRIMARY KEY,
THINGS TEXT NOT NULL,
HAS_DUPLICATES INT NULL
);
INSERT evilThings(ID,THINGS) VALUES
(1,"'AAA, BBB, AAA'"),
(2,"'CCC, DDD'");
CREATE TABLE notEvilAssocTable
( ai INT AUTO_INCREMENT PRIMARY KEY, -- no shuffle on inserts
ID INT NOT NULL,
THING VARCHAR(100) NOT NULL,
UNIQUE KEY `unqK_id_thing` (ID,THING) -- no dupes, this is honorable
);
Stored Proc:
DROP PROCEDURE IF EXISTS splitEm;
DELIMITER $$
CREATE PROCEDURE splitEm()
BEGIN
DECLARE lv_ID,pos1,pos2,comma_pos INT;
DECLARE lv_THINGS TEXT;
DECLARE particle VARCHAR(100);
DECLARE strs_done INT DEFAULT FALSE; -- string search done
DECLARE done INT DEFAULT FALSE; -- cursor done
DECLARE cur111 CURSOR FOR SELECT ID,THINGS FROM evilThings ORDER BY ID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- Please note in the above, CURSOR stuff MUST come LAST else "Error 1337: Variable or condition decl aft curs"
-- -------------------------------------------------------------------------------------------------------------------
TRUNCATE TABLE notEvilAssocTable;
OPEN cur111;
read_loop: LOOP
SET strs_done=FALSE;
FETCH cur111 INTO lv_ID,lv_THINGS;
IF done THEN
LEAVE read_loop;
END IF;
SET pos1=1,comma_pos=0;
WHILE !strs_done DO
SET pos2=LOCATE(',', lv_THINGS, comma_pos+1);
IF pos2=0 THEN
SET pos2=LOCATE("'", lv_THINGS, comma_pos+1);
IF pos2!=0 THEN
SET particle=SUBSTRING(lv_THINGS,comma_pos+1,pos2-comma_pos-1);
SET particle=REPLACE(particle,"'","");
SET particle=TRIM(particle);
INSERT IGNORE notEvilAssocTable (ID,THING) VALUES (lv_ID,particle);
END IF;
SET strs_done=1;
ELSE
SET particle=SUBSTRING(lv_THINGS,comma_pos+1,pos2-comma_pos-1);
SET particle=REPLACE(particle,"'","");
SET particle=TRIM(particle);
INSERT IGNORE notEvilAssocTable (ID,THING) VALUES (lv_ID,particle);
SET comma_pos=pos2;
END IF;
END WHILE;
END LOOP;
CLOSE cur111; -- close the cursor
END$$
DELIMITER ;
Test:
call splitEm();
See results of split:
select * from notEvilAssocTable;
Note that position 3, the InnoDB gap (from INSERT IGNORE). It is simply the innodb gap anomaly, an expected side effect like so many of InnoDB. In this case driven by the IGNORE part that creates a gap. No problem though. It forbids duplicates in our new table for split outs. It is common. It is there to protect you.
If you did not mean to have the single quote at the beginning and end of the string in the db, then change the routine accordingly.
Here is the answer to my question, assuming the data in THINGS field are separated by a bar '|'. Our original table will be myTABLE:
ID THINGS THINGSCount THINGSCountUnique HAS_DUPLICATES
1 AAA|BBB|AAA NULL NULL NULL
2 CCC|DDD NULL NULL NULL
Step 1. Check the maximum number of values separated by a bar '|' in THINGS field:
SELECT ROUND((CHAR_LENGTH(THINGS) - CHAR_LENGTH(REPLACE(THINGS,'|',''))) / CHAR_LENGTH('|')) + 1 FROM myTABLE;
Step 2. Assuming the answer from step 1 was 7, now use the following SQL to split the data in THINGS field into rows, there are many other approaches which you can Google to do the split:
CREATE TABLE myTABLE_temp
SELECT ID, SUBSTRING_INDEX(SUBSTRING_INDEX(myTABLE.THINGS, '|', n.n), '|', -1) THINGS
FROM myTABLE JOIN
( SELECT n FROM
( SELECT 1 AS N UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 ) a ) n
ON CHAR_LENGTH(THINGS) - CHAR_LENGTH(REPLACE(THINGS, '|', '')) >= n - 1
ORDER BY ID;
Our myTABLE_temp table will be something like:
ID THINGS
1 AAA
1 BBB
1 AAA
2 CCC
2 DDD
Step 3. Here we create two new tables to hold COUNT(THINGS) and COUNT(DISTINCT THINGS) as following:
# THINGSCount
CREATE TABLE myTABLE_temp_2
SELECT ID, COUNT(THINGS) AS THINGSCount FROM myTABLE_temp GROUP BY ID;
# Remember to ADD INDEX to ID field
UPDATE myTABLE A INNER JOIN myTABLE_temp_2 B ON(A.ID = B.ID) SET A.THINGSCount = B.THINGSCount;
# THINGSCountUnique
CREATE TABLE myTABLE_temp_3
SELECT ID, COUNT(THINGS) AS THINGSCountUnique FROM myTABLE_temp GROUP BY ID;
# Remember to ADD INDEX to ID field
UPDATE myTABLE A INNER JOIN myTABLE_temp_3 B ON(A.ID = B.ID) SET A.THINGSCountUnique = B.THINGSCountUnique;
Final Step: Flag duplicate values:
UPDATE myTABLE SET HAS_DUPLICATES = IF(THINGSCount>THINGSCountUnique, 'DUPLICATES', 'NO');

WHILE LOOP not working as intended in mysql PROCEDURE

I have 100 rows in table tbl_master_sales and an empty table tbl_customer_sales.When I use WHILE loop to insert data from tbl_master_salesto tbl_customer_sales,it only inserts 50 rows.However,it should have insert 100 rows taking two iteration of while loop.What may be my mistake in following PROCEDURE:
CREATE PROCEDURE ROWPERROW()
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
SELECT COUNT(*) FROM tbl_master_sales INTO n;
SET i=0;
WHILE i<n DO
INSERT INTO tbl_customer_sales (id,card_number,customer_name,customer_phone,bill_no,item_code,division,section,department,item_name,store,promo_name,billdiscount_name,billqty,promo_amount,bill_discount_amount,loyaltyamount,net_amount)
SELECT id, card_number, customer_name, customer_mobile, billno, itemcode, division, section, department, itemname, store, promoname, billdiscountname, billqty, promoamount, billdiscountamount, loyaltyamount, netamount
FROM tbl_master_sales
WHERE NOT EXISTS(SELECT 1
FROM tbl_customer_sales
WHERE id=tbl_master_sales.id)
LIMIT i,50;
SET i = i + 50;
END WHILE;
End;;
I don't see anything wrong with your procedure code logic but the reason for inserting only 50 rows could be the NOT EXISTS part shown below, which is restricting from inserting duplicate rows (or) filtering out the rest records.
WHERE NOT EXISTS(
SELECT 1
FROM tbl_customer_sales
WHERE id=tbl_master_sales.id)

MySQL AUTO INCREMENT with Prefix (YYMM) that reset every month

I saw another post explaining the use of sequence table to create prefixed AUTO INCREMENT ID.
Link to referenced post
SQL Fiddle
http://sqlfiddle.com/#!2/0ed88/1
I need the generated auto increment format to be: YYMM + AUTO INCREMENT VALUE that reset every month. For example now is January, 2015. The generated id should be: 15011, 15012, 15013, etc. Next month February 2015, the generated id should be: 15021, 15022, 15023, etc. I can use the above method to generate the prefix, however how do I reset the AUTO INCREMENT value each month? Database is InnoDB. Any help will be greatly appreciated :)
MODIFIED CODE
CREATE TABLE table1_seq
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
)|
CREATE TABLE Table1
(
id VARCHAR(7) NOT NULL PRIMARY KEY DEFAULT '0', name VARCHAR(30)
)|
CREATE TRIGGER tg_table1_insert
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
INSERT INTO table1_seq VALUES (NULL);
SET NEW.id = CONCAT(DATE_FORMAT(NOW(),'%y%m'), LAST_INSERT_ID());
END |
INSERT INTO Table1 (name) VALUES ('Jhon'), ('Mark')|
If you have a field DATE or DATETIME in your table then this solution maybe can help you.
Let say your table name my_table and has PK id (INT) and also a field bila (DATE). I just need to get last (biggest) id in current month from my_table then add by 1.
SET #mos= (SELECT MAX(id) FROM my_table WHERE MONTH(`bila`) = MONTH(NOW()) AND YEAR(`bila`) = YEAR(NOW()));
SET #mos = IF(
#mos IS NULL,
CONCAT(YEAR(NOW()),MONTH(NOW()),'001'),
#mos + 1
);
Then u can use #mos in your query next
To reset the AUTO_INCREMENT value, drop and recreate the table1_seq table. Given your example, a TRUNCATE statement would be sufficient (given that there aren't any foreign keys, and we assume the table is using either the MyISAM or InnoDB engine).
TRUNCATE TABLE table1_seq ;
(MySQL behavior for this statement is to create a new, empty table, with the AUTO_INCREMENT set back to the value when the table was created.)
This effectively achieves the same result as a DROP table followed by CREATE table.
That answers the question you asked. As a side note, generating a key value this way usually turns out to be a bad idea.
i've relied on the last answer above, i've used the orderDate field from my table and compared it with the current date during insertion , to decide finally if i want to reset or increment the purchaseOrderRef field:
The aim is to insert custom auto increment order id (field name: "purchaseOrderRef" ) with the format DD-MM-XXX in table [Products_SumOrders]. such that it resets the XXX to 0 automatically every month:
USE [Mydatabase] -- here you need to use your own database
GO
/****** Object: Trigger [dbo].[customAutoIncrement] Script Date: 10/1/2016 10:07:41 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[customAutoIncrement]
ON [dbo].[Products_SumOrders]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
declare #maxOrderID int;
declare #maxOrderRef nvarchar(max);
declare #relativeID varchar(max);
declare #orderId int;
select #orderId =orderid from inserted;
print(#orderId);--allow me to display the inserted order id
SET #maxOrderID = (SELECT Max(orderid) FROM [Products_SumOrders] WHERE MONTH([OrderDate]) = (MONTH(GETDATE())) AND YEAR([OrderDate]) = YEAR(GETDATE()) and orderid < #orderId );
print(#maxOrderID);--last added order
--custom month format (always MM):
declare #mon int;
declare #stringMon nvarchar(10);
set #mon= MONTH(GETDATE());
IF #mon <10
set #stringMon ='0' + CONVERT(VARCHAR(1),#mon) ;
ELSE
set #stringMon = CONVERT(VARCHAR(2),#mon) ;
IF #maxOrderID is null --no orders has been added in this month:
begin
set #maxOrderRef = Substring(CONVERT(VARCHAR(4),YEAR(GETDATE())),3,4)+ '-'+ CONVERT(VARCHAR(2),#stringMon)+'-001';
end
ELSE
--custom order id format (always XXX):
begin
set #relativeID =(SELECT [purchaseOrderRef] FROM [Products_SumOrders] WHERE orderid=#maxOrderID);
set #relativeID = Substring(#relativeID,LEN(#relativeID)-(Charindex('-', REVERSE(#relativeID))-2),LEN(#relativeID));
print(CONVERT(int,#relativeID));
IF CONVERT(int,#relativeID) < 9
set #relativeID ='00' + CONVERT(VARCHAR(2),#relativeID+1) ;
ELSE
begin
if CONVERT(int,#relativeID) < 99
set #relativeID ='0' + CONVERT(VARCHAR(3),#relativeID+1) ;
else
set #relativeID = CONVERT(VARCHAR(3),#relativeID+1) ;
end
set #maxOrderRef = Substring(CONVERT(VARCHAR(4),YEAR(GETDATE())),3,4)+ '-'+ CONVERT(VARCHAR(2),#stringMon)+'-'+ CONVERT(VARCHAR(3),#relativeID);
end
print(#maxOrderRef);
UPDATE Products_SumOrders
SET purchaseOrderRef = #maxOrderRef
FROM inserted INNER JOIN [Products_SumOrders] On inserted.orderid = [Products_SumOrders].orderid
END
GO

how to autoincrement varchar datatype depending upon the product detail's name using trigger

I have a product details table, the fields in the table are *Prod_Id, Product_Name, Quantity, Prefix, Slno*. Now i have created trigger for varchar data type field and implemented auto increment for Prod_Id (Primary key) and for Slno. The output will be
Prod_Id -->>> PROD0001, PROD0002 and so on. Now if the product name is RACK it should create a id RACK_001 with the start two letters of the product name and auto increment automatically. For all Product name it should create accordingly,how to do this.
As of now i am using a trigger for auto increment in varchar data type. Help with the trigger code.
DELIMITER $$
CREATE TRIGGER tg_product_details_INSERT
BEFORE INSERT ON product_details
FOR EACH ROW
BEGIN
INSERT INTO product_details_seq VALUES (NULL);
SET NEW.Created_Date = NOW();
SET NEW.Submitted_Date = NOW();
SET NEW.Slno = coalesce((select max(Slno) from product_details), 0) + 1;
SET NEW.Prod_id = CONCAT((NEW.Prefix), LPAD(LAST_INSERT_ID(), 3, '0'));
END
DELIMITER ;
product name -->> BOOK, SHOE, DRESS, FURNITURE
for all book it should create a auto increment id BOOK_001, BOOK_002 and for all Shoe it should create a auto increment id like SHOE_001, SHOE_002 and so on...!!!
Thanks,
Acube.
You can get the the Product Name using NEW.Product_Name.
Find the substring, i.e. the first two letters. Refer this link.
Then concat the result with while doing a SET NEW.Prod_id = ...
CONCAT('PROD', LPAD(LAST_INSERT_ID(), 3, '0'));
Why are you doing an insert into the same table? I think the rest of the code should work, without that:
DELIMITER $$
CREATE TRIGGER tg_product_details_INSERT
BEFORE INSERT ON product_details
FOR EACH ROW
BEGIN
SET NEW.Created_Date = NOW();
SET NEW.Submitted_Date = NOW();
SET NEW.Slno = coalesce((select max(Slno) from product_details), 0) + 1;
SET NEW.Prod_id = CONCAT('PROD', NEW.SLno, 3, '0'));
END$$
DELIMITER ;
This is manually setting up the slno and the prod_id using the same value, which should be one larger than the value in the table. I suppose that you could also query the metadata for the next auto-increment value as well:
select AUTO_INCREMENT
from information_schema.tables
where table_name = 'product_details' and table_schema = database();

Create insert trigger to auto increment int field of composite PK (String, int), restart numbering at 1 for new Strings

I've read that this can be done without issue using MyISAM as it is the default behavior , but I'm using InnoDB so need a trigger for such.
The two PK fields are batch and lineItem. If a record is deleted I want the numbering to start from the largest integer for batch. Not fill in the holes.
This is to set up a testing environment for a legacy system. So the schema is the way it is, I thought I'd mention that to avoid any discussion about whether it is good or not.
Edit: I want something like the following insert statement as a trigger
INSERT INTO payroll(`batch`,`lineItem`)
(select 'T105',t1.lineItem + 1 from payroll as t1 where batch = 'T105' order by lineItem desc limit 1);
But where 'T105' (the batch id) is hard coded I want the trigger to pick that up from the insert.
So I want to be able to say something like:
INSERT INTO payroll(`batch`)VALUES('T001','T001','T001', 'T002', 'T002', 'T002');
and I would expect to see in the table:
batch lineItem
T001 1
T001 2
T001 3
T002 1
T002 2
T002 3
Getting further:
In trying to implement this I've come up with:
DELIMITER $$
CREATE TRIGGER `co05_test`.`ins_lineItem`
BEFORE INSERT ON `co05_test`.`my_table`
FOR EACH ROW
BEGIN
select lineItem + 1 into #newLineItem from my_table where batch = NEW.batch order by lineItem desc limit 1;
set NEW.lineItem = #newLineItem;
END$$
However when I try...
INSERT INTO `co05_test`.`my_table`(`batch`)VALUES('T001');
I get this error: Column 'lineItem' cannot be null
Which is defined as not being nullable but I though the trigger should set the value!
Solution which I used:
-- Trigger DDL Statements
DELIMITER $$
USE `co05_test`$$
CREATE TRIGGER `co05_test`.`ins_lineItem`
BEFORE INSERT ON `co05_test`.`my_table`
FOR EACH ROW
BEGIN
select count(*) into #batchCount from my_table where batch = NEW.batch;
select lineItem + 1 into #newLineItem from my_table where batch = NEW.batch order by lineItem desc limit 1;
if #batchCount > 0 then
set NEW.lineItem = #newLineItem;
else
set NEW.lineItem = 1;
end if;
END;
$$
Have you tried declaring the variable instead?
DELIMITER $$
CREATE TRIGGER `co05_test`.`ins_lineItem`
BEFORE INSERT ON `co05_test`.`my_table`
FOR EACH ROW
BEGIN
DECLARE newLineItem INT;
SELECT
lineItem + 1 into newLineItem
FROM my_table
WHERE batch = NEW.batch
ORDER BY lineItem DESC
LIMIT 1;
SET NEW.lineItem = newLineItem;
END$$