How to add unique random values to the column in mysql - mysql

I want to add 1000 unique random values to the column AgentID to the table agents.
I have used Upsert to ignore the duplicate values. What I have done so far:
DROP PROCEDURE IF EXISTS Generate_random;
DELIMITER $$
CREATE PROCEDURE Generate_random()
BEGIN
Drop table if exists ai_training.`Agents`;
CREATE TABLE ai_training.`Agents`(AgentID int PRIMARY KEY);
SET #first = 1;
SET #last = 1000;
WHILE(#first <= #last) Do
INSERT INTO ai_training.`Agents` VALUES(
FLOOR(RAND()*(2900000-2800000+1)+2800000)
)
ON DUPLICATE KEY UPDATE AgentID = FLOOR(RAND()*(2900000-2800000+1)+2800000);
SET #first = #first + 1;
END WHILE;
END$$
DELIMITER ;
CALL Generate_random();
Select * from ai_training.`Agents`;
The problem is I need 1000 unique agentid's and this code is generating 1000 - the repeated ones. So, if it finds 6 repeated ids it returns 994 rows and not 1000.
Is there any way I can achieve this?
Can't change the random ID generation part.

You could use information function ROW_COUNT() to check whether a row was actually inserted or not, and increment the counter accordingly.
The documentation states:
With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row, 2 if an existing row is updated, and 0 if an existing row is set to its current values.
So:
WHILE(#first <= #last) DO
INSERT INTO ai_training.`Agents`
VALUES(FLOOR(RAND()*(2900000-2800000+1)+2800000))
ON DUPLICATE KEY UPDATE AgentID = AgentID;
IF ROW_COUNT() = 1 THEN
SET #first = #first + 1;
END IF;
END WHILE;
Note that I changed the action on duplicate key to a dummy update rather than re-assigning a new random value; this makes things a little less expensive when a collision happens.

CREATE PROCEDURE Generate_random()
BEGIN
Drop table if exists ai_training.`Agents`;
CREATE TABLE ai_training.`Agents`(AgentID int PRIMARY KEY);
SET #last = 1000;
WHILE (SELECT COUNT(*) FROM ai_training.`Agents`) < #last DO
INSERT IGNORE INTO ai_training.`Agents`
VALUES
(FLOOR(RAND()*(2900000-2800000+1)+2800000));
END WHILE;
END

Related

Are mysql triggers atomic? Does this mysql trigger have concurrency issues?

I have created the following trigger to populate a field with a unique integer value.
I am using this in an InnoDB table and there is a UNIQUE key constraint on the field.
Is it possible that two concurrent inserts produce the same value and due to the unique constraint one of them fails, or are triggers "atomic"?
Are there any other issues with this code that I may not have thought of?
Is there a better whay to get the behaviour I want? Maby some soft of isomorphism on the auto increment value?
CREATE TRIGGER `generate_customer_id` BEFORE INSERT ON `users`
FOR EACH ROW BEGIN
DECLARE i INT;
DECLARE duplicate INT DEFAULT 1;
DECLARE tries INT DEFAULT 0;
WHILE duplicate > 0 DO
SET tries = tries + 1;
IF tries > 100 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'no customer id found after 100 tries', MYSQL_ERRNO = 1001;
END IF;
SET i = ROUND((RAND() * (999999999-100000000))+100000000);
SET duplicate = (SELECT COUNT(*) FROM users WHERE customer_id = i);
END WHILE;
SET NEW.customer_id = i;
END
A possible alternative to the random number generator is to create a customer_ids table with an auto_increment primary key - create a new row in the table to create a unique customer_id for the users table

IF NOT EXISTS in mysql showing syntax error

I am trying to convert this tsql to mysql but showing error need help
CREATE PROCEDURE FormAdd
#formName varchar(MAX)
AS
IF NOT EXISTS(SELECT * FROM tbl_Form WHERE formName=#formName)
BEGIN
INSERT INTO tbl_Form
(formName)
VALUES
(#formName)
SELECT ##identity
END
ELSE
BEGIN
SELECT '-1'
END
mysql
CREATE PROCEDURE FormAdd
(p_formName varchar(500) )
begin
INSERT INTO tbl_Form (formName)
VALUES (p_formName)
where NOT EXISTS(SELECT * FROM tbl_Form WHERE formName=p_formName) ;
SELECT Last_insert_id() as returnvalue ;
SELECT '-1' ;
end
Your attempt was syntactically invalid because logically, an INSERT statement cannot contain a WHERE clause since it does not act on existing rows.
If the purpose is to insert only if the value for p_formname is not already present, then an appropriate step would be to define a unique index on that column first. Then, construct your procedure to attempt the insert and inspect the ROW_COUNT() value to see if one was inserted and act accordingly, returning -1 if not to adapt your existing T-SQL procedure.
First create the unique index on p_formname:
ALTER TABLE tbl_Form ADD UNIQUE KEY `idx_formName` (`formName`);
Then your procedure should use INSERT INTO...ON DUPLICATE KEY UPDATE to attempt to insert the row. Per the documentation, the value of ROW_COUNT() will be 0 if a new row was not inserted or 1 if it was.
CREATE PROCEDURE FormAdd (p_formName varchar(500))
BEGIN
/* Attempt the insert, overwrite with the same value if necessary */
INSERT INTO tbl_Form (formName) VALUES (p_formName) ON DUPLICATE KEY UPDATE formName = p_formName;
/* Return the LAST_INSERT_ID() for a new row and -1 otherwise */
SELECT
CASE
WHEN ROW_COUNT() = 1 THEN LAST_INSERT_ID()
ELSE -1
END AS returnValue;
END

mysql trigger error with 2 conditions

I want to add an after insert trigger which will do the following.
The first IF condition works normally, but when it comes to the second everything stops.
Any ideas?
USE `Syslog`;
DELIMITER $$
CREATE TRIGGER `SystemEventsR_AINS` AFTER INSERT ON SystemEventsR FOR EACH ROW
IF
(exists
(select syslogtag from SystemEventsRcounter where syslogtag=
new.syslogtag)
AND
(select simpledate from SystemEventsRcounter
where syslogtag=new.syslogtag)=new.simpledate)
THEN
UPDATE SystemEventsRcounter
SET records=records+1
WHERE SystemEventsRcounter.syslogtag=new.syslogtag;
ELSE INSERT SystemEventsRcounter (simpledate, syslogtag, records) values (new.simpledate,new.syslogtag,1);
END IF
UPDATED:
What you need is INSERT INTO ... ON DUPLICATE KEY.
CREATE TRIGGER `SystemEventsR_AINS`
AFTER INSERT ON SystemEventsR
FOR EACH ROW
INSERT INTO SystemEventsRcounter (simpledate, syslogtag, records)
VALUES (NEW.simpledate, NEW.syslogtag, 1)
ON DUPLICATE KEY UPDATE records = records + 1;
In order for it to work you need to create a unique composite index on (simpledate, syslogtag)
CREATE UNIQUE INDEX idx_u_simpledate_syslogtag
ON SystemEventsRcounter (simpledate, syslogtag);
Here is SQLFiddle demo.
If you wanted it your way then it might look like
DELIMITER $$
CREATE TRIGGER `SystemEventsR_AINS`
AFTER INSERT ON SystemEventsR
FOR EACH ROW
BEGIN
IF (
SELECT COUNT(*) simpledate
FROM SystemEventsRcounter
WHERE syslogtag = NEW.syslogtag
AND simpledate = NEW.simpledate
) > 0 THEN
UPDATE SystemEventsRcounter
SET records = records + 1
WHERE SystemEventsRcounter.syslogtag = NEW.syslogtag;
ELSE
INSERT INTO SystemEventsRcounter (simpledate, syslogtag, records)
VALUES (NEW.simpledate, NEW.syslogtag, 1);
END IF;
END$$
DELIMITER ;
Here is SQLFiddle demo.

How to fix this MySQL trigger?

I'm trying to get this trigger to work:
CREATE TRIGGER Transaction_insert BEFORE INSERT ON Transaction
FOR EACH ROW WHERE Number = NEW.AccountNumber
IF Account.CreditBalance + NEW.Amount < Account.CreditLimit THEN
UPDATE Account SET CreditBalance = CreditBalance + NEW.Amount where Number = NEW.AccountNumber;
ELSE
SET NEW.Valid = 0
END IF;
This is the error I get from myPHPAdmin.
Your IF needs to be a full SELECT to reference another table (Account)
IF EXISTS (SELECT * FROM `Account` A
WHERE A.CreditBalance + NEW.Amount < A.CreditLimit AND
A.Number = NEW.AccountNumber) THEN
UPDATE ...
Edit: this was on your 2nd duplicate answer
In this case, remove the WHERE after FOR EACH ROW
Updated Answer
This is what I think you want, assuming that Account to Transaction is a 1:N relationship keyed on Number/AccountNumber:
DELIMITER //
-- Assumptions:
-- 1. Transaction.AccountNumber is F.K. REFERENCES Account(Number)
-- 2. Account.Number is UNIQUE
--
CREATE TRIGGER trg_bi_transaction BEFORE INSERT ON Transaction
FOR EACH ROW
BEGIN
-- Adjust account balance (if permitted)
--
UPDATE Account
SET CreditBalance = CreditBalance + NEW.Amount
WHERE Number = NEW.AccountNumber
AND
(CreditBalance + NEW.Amount) < CreditLimit;
-- Was the adjustment valid/permitted?
--
SET NEW.Valid = (ROW_COUNT() = 1);
END //
DELIMITER ;
That trigger will attempt to UPDATE the proper Account for any given Transaction if the CreditLimit permits. The Valid field will be set to 1 if the UPDATE succeeded, and 0 if it did not.
Original Answer
MySQL triggers do not support trigger-level WHERE clauses. Move the Number/NEW.AccountNumber check inside the trigger body.

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$$