Table:
create table produto
(
referencia varchar(3) primary key,
descricao varchar(50) unique,
estoque int not null default 0,
preco_normal decimal(10,2),
preco_desconto decimal(10,2)
);
Trigger (I tried creating it without a delimiter to no avail as well):
delimiter //
create trigger desconto before insert
on produto
for each row
begin
if new.estoque < 5 then
set new.preco_desconto = new.preco_normal * 0.90;
else
set new.preco_desconto = new.preco_normal;
end if;
end//
delimiter ;
The trigger result:
But when I use drop trigger if exists desconto;
The output says Trigger does not exist
Any ideas on how to solve this?
Related
Challenge:
Create a method to set "auto_increment" values for tables in a non-sequential way.
The goal is to override the "auto_increment" mechanism and allow the function "LAST_INSERT_ID()" to continue working as expected (returning an INT), so that no changes are needed in software side.
My Solution
The method I found is based on an auxiliary table (unique_id), that stores values available to be assigned. Values are then selected randomly, and removed from the tables as used. When the table gets empty, a new set of ID's is created.
This example is working as expected, but with one problem.
Tables for the demo:
CREATE TABLE `unique_id` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=100;
CREATE TABLE `test_unique_id` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;
Defined a stored procedure and a function:
DELIMITER $$
DROP PROCEDURE IF EXISTS `UNIQUE_ID_REFILL`$$
CREATE PROCEDURE UNIQUE_ID_REFILL()
BEGIN
DECLARE a INT Default 0 ;
simple_loop: LOOP
SET a=a+1;
INSERT INTO unique_id (id) values(null);
IF a=100 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
END $$
DROP FUNCTION IF EXISTS `UNIQUE_ID_GET`$$
CREATE FUNCTION UNIQUE_ID_GET()
RETURNS INT(11)
MODIFIES SQL DATA
BEGIN
DECLARE new_id INT(11);
DECLARE unique_id_count INT(11);
SET new_id = 0;
SELECT COUNT(*) INTO unique_id_count FROM unique_id;
IF unique_id_count=0 THEN
CALL UNIQUE_ID_REFILL();
END IF;
SELECT id INTO new_id FROM unique_id ORDER BY RAND() LIMIT 1;
DELETE FROM unique_id WHERE id = new_id;
RETURN new_id;
END $$
Created a Trigger on the destination table (test_unique_id):
CREATE TRIGGER test_unique_id__unique_id BEFORE INSERT ON test_unique_id
FOR EACH ROW
SET NEW.id = UNIQUE_ID_GET();
The solution is getting the random ID's as expected:
INSERT INTO test_unique_id(name) VALUES ('A'),('B'),('C');
Creates the rows:
id name
154 'A'
129 'B'
173 'C'
The Problem
The main problem is that LAST_INSERT_ID() stops working... and the software side is broken:
SELECT LAST_INSERT_ID();
0
Any ideas on how to solve this problem? or any other different approach to the challenge?
Thank you very much.
I'm having difficulty with developing the logic in MySQL. I don't know how to INSERT multiple records from a Table AFTER UPDATE.
CREATE TABLE primeira(
ID int PRIMARY KEY AUTO_INCREMENT,
nome varchar(30) NOT NULL,
valor int DEFAULT 0
);
CREATE TABLE segunda(
ID int PRIMARY KEY AUTO_INCREMENT,
ID_primeira int,
ultimo_valor int DEFAULT 0,
credito int NOT NULL,
limite int DEFAULT 0,
FOREIGN KEY(ID_primeira) references primeira(ID)
);
CREATE TABLE terceira(
ID int PRIMARY KEY AUTO_INCREMENT,
ID_segunda int,
`data` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
estado boolean DEFAULT false,
FOREIGN KEY(ID_segunda) references segunda(ID)
);
CREATE TRIGGER tr_segundaLimite_INS
BEFORE INSERT ON segunda FOR EACH ROW
SET NEW.limite = New.ultimo_valor + New.credito;
DELIMITER //
CREATE TRIGGER tr_primeira_UPD
AFTER UPDATE ON primeira FOR EACH ROW
IF (SELECT limite FROM segunda WHERE segunda.ID_primeira = New.ID AND
(limite - NEW.valor)< 50) THEN
INSERT INTO terceira(ID_segunda)
VALUES ((SELECT ID FROM segunda WHERE segunda.ID_primeira = New.ID
AND (limite - NEW.valor)< 50));
END IF;
END
//
DELIMITER ;
I'm going to use procedures with functions to SELECT the data. The problem with this TRIGGER is that it's not working when there are multiple matching records.
The error that I am getting is-
subquery returns more than 1 row.
The objective is: After an update of the primeira.valor, the trigger would subtract segunda.limite - New.valor. If this difference is < 50 then all the matching segunda.ID would be registered at terceira.ID_segunda on terceira table.
I'm using data below:
INSERT INTO primeira(nome,valor)
VALUES
('Burro',800),
('Chiconizio',300),
('Xerosque',400),
('Shrek',600);
INSERT INTO segunda(ID_primeira,ultimo_valor,credito)
VALUES
(1,600,800),
(1,700,400),
(1,800,500),
(2,150,200),
(2,200,180),
(2,250,300);
UPDATE primeira
SET Valor = 330
WHERE ID = 2;
You need CURSOR for this. You can try the following trigger code. I hope this will fix your issue.
DELIMITER //
CREATE TRIGGER tr_primeira_UPD
AFTER UPDATE ON primeira FOR EACH ROW
BEGIN
DECLARE v_limite_diff INT;
DECLARE v_seg_id INT;
DECLARE done INT DEFAULT FALSE;
DECLARE c1 CURSOR FOR SELECT (s.limite - NEW.valor), s.id
FROM segunda s WHERE s.ID_primeira = New.ID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN c1;
my_loop: LOOP
FETCH c1 INTO v_limite_diff, v_seg_id;
IF done THEN
LEAVE my_loop;
END IF;
IF( v_limite_diff < 50 ) THEN
INSERT INTO terceira(ID_segunda) VALUES(v_seg_id);
END IF;
END LOOP;
END//
DELIMITER ;
I created two columns in student table for my database. I now need to create a cursor for the student table. I need to make use of the input parameters for start and end student Id’s to filter the results in the cursor query.
I also need to open the cursor created and check if the student has an email assigned or not. If the email is not assigned I need to update the email column to have an email assigned to the student.
Finally to why I want help:
I cannot figure out how I would write the case statements for this cursor. I have tried to think of a good way to do this and not succeeded. Please help!!
DELIMITER $
DROP PROCEDURE IF EXISTS CURSOR_DEMO$
CREATE PROCEDURE CURSOR_DEMO(start_student_id INT
,end_student_id INT
)
BEGIN
DECLARE l_table_name VARCHAR(50);
DECLARE iam_done INT DEFAULT 0;
DECLARE l_sql_stmt VARCHAR(5000);
SET #l_sql_stmt='ALTER TABLE STUDENT ADD EMAIL VARCHAR';
SELECT #l_sql_stmt;
prepare stmt from #l_sql_stmt;
execute stmt;
SET #l_sql_stmt='ALTER TABLE STUDENT ADD PHONE int(10)';
SELECT #l_sql_stmt;
prepare stmt from #l_sql_stmt;
execute stmt;
DECLARE TBL_CUR CURSOR FOR
SELECT EMAIL FROM STUDENT.TABLES WHERE TABLE_SCHEMA='MYSQLDB';
BEGIN
DECLARE CONTINUE HANDLER FOR NOT FOUND SET IAM_DONE=1;
OPEN TBL_CUR;
tbl_loop:LOOP
FETCH tbl_cur INTO l_table_name;
IF IAM_DONE = 1 THEN
LEAVE tbl_loop;
END IF;
CASE WHEN l_table_name = 'STUDENT' THEN
ELSE BEGIN END;
END CASE;
END LOOP tbl_loop;
CLOSE TBL_CUR;
END;
END$
DELIMITER ;
Is this the kind of thing you need?
/*
drop table student;
delimiter $$
CREATE TABLE `student` (
`id` int(11) NOT NULL ,
`name` char(1) NOT NULL,
`email` varchar(1),
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8$$
drop table email;
delimiter $$
CREATE TABLE `email` (
`id` int(11) NOT NULL ,
`type` varchar(8) NOT NULL,
`person_id` int,
`email` varchar(1),
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8$$
;
*/
DELIMITER $
DROP PROCEDURE IF EXISTS CURSOR_DEMO$
CREATE PROCEDURE CURSOR_DEMO(start_student_id INT
,end_student_id INT)
BEGIN
declare email_person_id int;
declare email_address varchar(50);
DECLARE done INT DEFAULT 0;
DECLARE CUR CURSOR FOR
SELECT person_id,email FROM sandbox.email where type = 'student';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
OPEN CUR;
cur_loop:LOOP
IF done = 1 THEN
LEAVE cur_loop;
END IF;
FETCH CUR INTO email_person_id,email_address;
update student
set email = email_address
where id = email_person_id
and email is null
;
END LOOP cur_loop;
CLOSE CUR;
commit;
END$
DELIMITER ;
truncate table student;
insert into student
values
(1,'A','1'),
(2,'B','1'),
(3,'C',null),
(4,'D',null),
(5,'E',null),
(6,'G',null),
(7,'F',null)
;
truncate table email;
insert into email
values
(1,'student',1,'a'),
(2,'student',2,'b'),
(3,'faculty',7,'z'),
(4,'student',3,'c')
;
select * from student;
call cursor_demo(1,10);
select * from student;
I'm trying to add a trigger for auditing to initialize a datetime field on an insert. Does anyone see what might be causing this trigger to not fire???
USE example;
CREATE TABLE USERS (
ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
FULLNAME VARCHAR(128) NOT NULL,
`PASSWORD` CHAR(88) NOT NULL,
EMAIL VARCHAR(128) NOT NULL,
FLAGS TINYINT UNSIGNED DEFAULT 0,
CREATED DATETIME,
UPDATED TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE INDEX(EMAIL),
PRIMARY KEY( ID )
);
DELIMITER $$;
CREATE TRIGGER USER_T BEFORE INSERT ON USERS FOR EACH ROW
BEGIN
SET NEW.CREATED = CURRENT_TIMESTAMP();
END;$$
DELIMITER ;
INSERT INTO USERS(FULLNAME, `PASSWORD`, EMAIL) VALUES('Admin', 'sQnzu7wkTrgkQZF+0G1hi5AI3Qmzvv0bXgc5THBqi7mAsdd4Xll27ASbRt9fEyavWi6m0QP9B8lThf+rDKy8hg==', 'root#localhost');
It looks like you are using the ; as a delimiter here
SET NEW.CREATED = CURRENT_TIMESTAMP();
END;$$
DELIMITER ;
Try this:
DELIMITER $$;
CREATE TRIGGER USER_T BEFORE INSERT ON USERS FOR EACH ROW
BEGIN
SET NEW.CREATED = CURRENT_TIMESTAMP();
END $$
DELIMITER ;
I am trying to get this procedure to work and it is stumping me. I simply want this procedure to populate a temp table with the separated values from a table of CSV values.
DELIMITER $$
DROP PROCEDURE IF EXISTS String_Split $$
CREATE PROCEDURE String_Split
(
vString VARCHAR(255),
vSeparator VARCHAR(5)
)
BEGIN
DECLARE vDone tinyint(1) DEFAULT 1;
DECLARE vIndex INT DEFAULT 1;
DECLARE vSubString VARCHAR(15);
DROP TABLE IF EXISTS tmpValues;
CREATE TEMPORARY TABLE tmpValues (tmpVal VARCHAR(255));
WHILE vDone > 0 DO
SET vSubString = SUBSTRING(vString, vIndex,
IF(LOCATE(vSeparator, vString, vIndex) > 0,
LOCATE(vSeparator, vString, vIndex) - vIndex,
LENGTH(vString)
));
IF LENGTH(vSubString) > 0 THEN
SET vIndex = vIndex + LENGTH(vSubString) + 1;
INSERT INTO tmpValues VALUES (vSubString);
ELSE
SET vDone = 0;
END IF;
END WHILE;
END; $$
I call on it:
CALL String_Split(my_csv.keywords, ',');
And I get this:
Error Code: 1109. Unknown table 'my_csv' in field list
I'm not getting this because the table is there and the appropriate database is selected.
CREATE TABLE `my_csv` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`keywords` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 $$
INSERT INTO `my_csv` () VALUES
(1, 'featured, 3/8, Diamond, Engagement Ring, 14K, White Gold, Gold'),
(2, '1/3, Diamond, Engagement Ring, 14K, White Gold, Gold'),
(3, 'featured') $$
Instead of calling the table name and field in the call function, you need to get the value first, then call the procedure. Here's a working example.
SELECT #keyword :=keywords from my_csv;
CALL String_Split(#keyword, ',');
Edit:
That only grabbed the last row. This one selects all of them. Also, doing this the vSeparator variable in the procedure was too small, so I bumped that up to 255.
SELECT #keyword :=group_concat(keywords) FROM my_csv;
CALL String_Split(#keyword, ',');
select * from tmpValues;