Trigger syntax and IF ELSE THEN - mysql

I'd like to create a trigger which count the number of rows with a specific id (id_ort).
If it found more than 5 rows, I need to increment a variable.
Trigger Syntax
BEGIN
DECLARE nb INT;
DECLARE nba INT;
SET nba =0;
SET NEW.`VPLS_ID_NodeB` = CONCAT("21100", LPAD(NEW.`VPLS_ID_NodeB`,4,0));
SET nb = (SELECT COUNT(DISTINCT(`VPLS_ID_aggregation`)) FROM `VPLS_nodeB` WHERE `id_ORT` = NEW.`id_ORT`);
IF(nb > 5) THEN
SET nba = nb + 1;
ELSE
SET nba = nb;
END IF;
SET NEW.`VPLS_ID_aggregation` = CONCAT("21188", LPAD(NEW.`id_ORT`,2,0), LPAD(nba,2,0));
END
However, there is a bug... Even if i've less than 5 rows, the var is incremented each time.
Any ideas? Maybe it's a syntax problem...
Thanks a lot!

you probably forgot to specify a delimiter i've also made a few other changes as you can see
delimiter #
create trigger VPLS_nodeB_before_ins_trig before insert on VPLS_nodeB
for each row
BEGIN
DECLARE nb INT default 0;
DECLARE nba INT default 0;
SET NEW.VPLS_ID_NodeB = CONCAT('21100', LPAD(NEW.VPLS_ID_NodeB,4,0));
SET nb = (SELECT COUNT(DISTINCT(VPLS_ID_aggregation)) FROM VPLS_nodeB WHERE id_ORT = NEW.id_ORT);
IF(nb > 5) THEN
SET nba = nb + 1;
ELSE
SET nba = nb;
END IF;
SET NEW.VPLS_ID_aggregation = CONCAT('21188', LPAD(NEW.id_ORT,2,0), LPAD(nba,2,0));
END#
delimiter ;

Related

If statement with multiple statements inside loops doesn't work

I have the follow procedure :
DELIMITER $$
CREATE PROCEDURE getCost(
in p_idp int(11),
out p_cost double)
BEGIN
DECLARE strt double;
DECLARE stop double;
DECLARE diff double;
DECLARE p_hs INTEGER;
DECLARE p_hf INTEGER;
DECLARE p_hcost double;
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE i INTEGER;
-- declare cursor
DECLARE tariffe_cursor CURSOR FOR
SELECT hs,hf,cost FROM tariffe ORDER BY hs;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished = 1;
SELECT UNIX_TIMESTAMP(data_inizio) INTO strt
FROM prenotazioni WHERE IdP = p_idp;
SET stop = UNIX_TIMESTAMP();
SET diff = stop - strt;
SET diff = diff / 3600;
SET i = 0;
SET p_cost = 0;
WHILE i < diff
DO
OPEN tariffe_cursor;
get_tariffe: LOOP
FETCH next FROM tariffe_cursor INTO p_hs,p_hf,p_hcost;
IF v_finished = 1 THEN
LEAVE get_tariffe;
END IF;
IF (i >= p_hs AND i < p_hf) THEN
SET p_cost = p_cost + p_hcost;
SET v_finished = 1;
END IF;
END LOOP get_tariffe;
CLOSE tariffe_cursor;
SET i = i + 1;
END WHILE;
IF diff < 0.25 THEN
SET p_cost = 0;
END IF;
END$$
DELIMITER ;
Table tariffe has 2 records:
hs = 0, hf = 3, hcost = 3
hs = 3, hf = 1000, hcost = 2
Suppose DIFF = 1.50 -> Expect p_cost to be 6
Suppose DIFF = 3.75 -> Expect p_cost to be 11
but p_cost is always 3
Tried to use some INSERTs (into a temp table) to check the WHILE loop and the cursor loop and realized that the
IF (i >= p_hs AND i < p_hf) THEN
is computed true only when i = 0 (first while loop) but when i > 0 is always computed as false.
F.ex. when i = 1 the first cursor FETCH returns hs=0,hf=3,hcost=3, but IF seems to be false
What I'm doing wrong?
I've also tried (with no success)
IF (i >= p_hs) AND (i < p_hf)
IF i >= p_hs AND i < p_hf
LOGIC:
Diff is the number of hours between start and stop (renting period).
Tariffe Table contains rental cost: from 0 to 3 hours -> 3 euros, from 3 upto 1000 -> 2 euros.
While loop iterates for every single hour of rental
Cursor loop check for the right hourly cost and add it to p_cost
The problem is that you never set v_finished back to 0 when you leave the get_tariffe loop and continue the while i < diff loop. So on subsequent iterations, if v_finished = 1 is always true and you leave the loop before adding to p_cost.
Put
SET v_vinished = 0;
after
OPEN tariffe_cursor;
But I don't think you'll ever get 11 as a result. You do SET v_finished = 1; after adding the first p_hcost, so it will never process the second row of the table. I'm not sure what your intended logic is to get 11. Maybe you should just take out the line that sets v_finished.

MySQL trigger on insert of text going into infinite loop

There are three tables: users, ap_name_bank_m and ap_name_bank_h. users table has three columns 'name_eng' (name of user in english), 'name_gdn_eng'(name of user's guardian in english) and 'category'.
ap_name_bank_m and ap_name_bank_h are corpus of names for each category of name: 'm' or 'h'.
When a row is updated I want to check if each word of both names in present in either of two tables ap_name_bank_m and ap_name_bank_h.
Whichever count is higher that category will be assigned. The below code which I have written is going into infnite loop and I am getting "MySQL server has gone away error". Can someone tell me where I am wrong?
Assume name_eng and name_gdn_eng will only contain words with spaces and nothing else.
DELIMITER $$
create trigger set_cat before update on users_table for each row
BEGIN
declare words text;
declare word varchar(50);
declare num_m int default 0;
declare num_h int default 0;
declare len int default 0;
set words = concat(new.name_eng,' ',new.name_gdn_eng);
iterator:
LOOP
set word = substring_index(words,' ',1);
set num_m = EXISTS(select 1 from ap_name_bank_m where name=word) + num_m;
set num_h = EXISTS(select 1 from ap_name_bank_h where name=word) + num_h;
set words = trim(replace(words,word,''));
END LOOP iterator;
if (num_m > num_h) then set new.category='M'; end if;
if (num_h > num_m) then set new.category='H'; end if;
END $$
DELIMITER ;
I found a better way to do this. No need for loop.
BEGIN
declare words text;
declare word varchar(50);
declare num_m int default 0;
declare num_h int default 0;
set words = concat(new.name_eng,' ',new.name_gdn_eng);
set num_h = (select count(*) from ap_name_bank_h where match(name) against (words in natural language mode));
set num_m = (select count(*) from ap_name_bank_m where match(name) against(words in natural language mode));
/*
iterator:
LOOP
set word = substring_index(words,' ',1);
set num_m = EXISTS(select 1 from ap_name_bank_m where name=word) + num_m;
set num_h = EXISTS(select 1 from ap_name_bank_h where name=word) + num_h;
set words = trim(replace(words,word,''));
END LOOP iterator;*/
if (num_m > num_h) then set new.category='M'; end if;
if (num_h > num_m) then set new.category='H'; end if;
END

MySQL IN Clause in SQL query with User Defined Function

I am using following query to update all the children of particular topic.
UPDATE topics SET reuse = 0 WHERE topic_id IN (SELECT GetChildTopics(187));
Where
SELECT GetChildTopics(187);
returns "188,190,189" but my update query is updating only first row with topic_id = 188, instead of updating first topic only, it should update all 3 topics.
When I put the values manually it works fine.
UPDATE topics SET reuse = 0 WHERE topic_id IN (188,190,189);
Can anyone suggest what's wrong I am doing here?
Here is the code for GetChildTopics MySQL Function
CREATE DEFINER=`root`#`localhost` FUNCTION `GetAncestry`(GivenID INT) RETURNS varchar(1024) CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE rv VARCHAR(1024);
DECLARE cm CHAR(1);
DECLARE ch INT;
SET rv = '';
SET cm = '';
SET ch = GivenID;
WHILE ch > 0 DO
SELECT IFNULL(parent_topic_id,-1) INTO ch FROM
(SELECT parent_topic_id FROM topic_list WHERE id = ch) A;
IF ch > 0 THEN
SET rv = CONCAT(rv,cm,ch);
SET cm = ',';
END IF;
END WHILE;
RETURN rv;
END
Try this;)
UPDATE topics SET reuse = 0 WHERE FIND_IN_SET(topic_id, GetChildTopics(187));

MySQL: Change case for compound names

I have a dataset where names are in all uppercase, and need to convert them to proper case for reports. I found here in Stackoverflow the following code:
SET LastName = CONCAT(UPPER(SUBSTRING(LastName, 1, 1)),LOWER(SUBSTRING(LastName, 2)));
This works great for simple last names:
SMITH --> Smith
JONES --> Jones
But not so good for compound names:
VAN DYKE --> Van dyke
CARTER-SMITH --> Carter-smith
Has anyone developed some MySQL code that can do the following:
VAN DYKE --> Van Dyke
CARTER-SMITH --> Carter-Smith
I know that we will not be able to catch every possible situation, but I hope someone has at least tackled converting names that are separated by dashes or spaces.
I saw this problem on another site, check it out: http://www.thingy-ma-jig.co.uk/blog/30-09-2010/mysql-how-upper-case-words
He uses a function. So I hope you have the rights to create one.
You guys are so helpful! The answer I came up with was:
CREATE FUNCTION CAP_FIRST (input VARCHAR(255))
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE len INT;
DECLARE i INT;
SET len = CHAR_LENGTH(input);
SET input = LOWER(input);
SET i = 0;
WHILE (i < len) DO
IF (MID(input,i,1) = ' ' OR MID(input,i,1) = '-' OR i = 0) THEN
IF (i < len) THEN
SET input = CONCAT(
LEFT(input,i),
UPPER(MID(input,i + 1,1)),
RIGHT(input,len - i - 1)
);
END IF;
END IF;
SET i = i + 1;
END WHILE;
RETURN input;
END;
And it works beautifully!
You would think that the world’s most popular open source database, as MySQL like to call itself, would have a function for making items title case (where the first letter of every word is capitalized). Sadly it doesn’t.
This is the best solution i found Just create a stored procedure / function that will do the trick
mysql>
DROP FUNCTION IF EXISTS proper;
SET GLOBAL log_bin_trust_function_creators=TRUE;
DELIMITER |
CREATE FUNCTION proper( str VARCHAR(128) )
RETURNS VARCHAR(128)
BEGIN
DECLARE c CHAR(1);
DECLARE s VARCHAR(128);
DECLARE i INT DEFAULT 1;
DECLARE bool INT DEFAULT 1;
DECLARE punct CHAR(17) DEFAULT ' ()[]{},.-_!#;:?/';
SET s = LCASE( str );
WHILE i <= LENGTH( str ) DO
BEGIN
SET c = SUBSTRING( s, i, 1 );
IF LOCATE( c, punct ) > 0 THEN
SET bool = 1;
ELSEIF bool=1 THEN
BEGIN
IF c >= 'a' AND c <= 'z' THEN
BEGIN
SET s = CONCAT(LEFT(s,i-1),UCASE(c),SUBSTRING(s,i+1));
SET bool = 0;
END;
ELSEIF c >= '0' AND c <= '9' THEN
SET bool = 0;
END IF;
END;
END IF;
SET i = i+1;
END;
END WHILE;
RETURN s;
END;
|
DELIMITER ;
then
update table set LastName = properword(LastName)
or
select proper( LastName ) as properLastName
from table

mysql trigger function

I have a table call lp_upload and it contain plate number of car and other related information:
CREATE TABLE `lp_upload` (
`date` date NULL ,
`plate` char(10) NULL ,
`site` int NULL ,
`dateid` char(20) NULL
)
;
this table is getting information from a traffic camera. however, sometime letter in the plate is not recognized and it will be replace by $. So if a plate is really abc123, but the camera didnt recognized c and 1, it will be ac$$23 that get enter into the table.
im suppose to make it so when a new plate is entered and 6 of its letters match an existing plate, it will become that plate. EX: 123$5678 is entered and 12345678 already exist, then 123$5678 will be replace by 12345678.
so i first wrote a match function:
CREATE DEFINER = CURRENT_USER FUNCTION `matchingfun`(`str1` char(10),`str2` char(10))
RETURNS int
BEGIN
DECLARE myindex int DEFAULT 0;
DECLARE count int DEFAULT 0;
DECLARE maxlength int;
SET maxlength = length(str1);
for_loop: LOOP
SET myindex = myindex + 1;
IF maxlength < myindex then
RETURN 0;
END IF;
IF SUBSTRING(str1,myindex,1)= SUBSTRING(str2,myindex,1)then
SET count = count +1;
END IF;
IF count > 6 then
RETURN 1;
END IF;
IF SUBSTRING(str1,myindex,1)!= SUBSTRING(str2,myindex,1) and SUBSTRING(str1,myindex,1)!= '$' and SUBSTRING(str2,myindex,1)!= '$'then
RETRUN 0;
END IF;
END LOOP for_loop;
RETURN 0;
END
and I added a trigger function to the table
CREATE TRIGGER `trigger1` AFTER INSERT ON `lpr_opt_upload`
BEGIN
declare old_site_id int;
declare old_text char(10);
select lpr_text into old_text from lpr_opt_upload where matchingfun(new.lpr_text, lpr_text) = 1;
if(old_text is not null) then
set new.lpr_text = old_text;
end if;
END
when i run this, the database crashes. can you help fix this problem or suggest a better way to do this. thanks.
I suspect that the problem you're running into is multiple matches. For example, if you have abcd01234 and abcde1234 in the database and attempt to insert abcd$1234, you'll get an error.
Now, I'm going to assume that this application is supposed to match OCR'd license plates from speed cameras or red-light cameras in order to facilitate ticketing of the vehicle owner. If that's the case, then you want to err on the side of caution and not have the system automatically try to pick from multiple candidates and instead have a real human look at the result and confirm the plate number.
So, operating on that assumption:
DELIMITER //
CREATE TRIGGER `attempt_match_existing_plate`
BEFORE INSERT
ON `lp_upload`
FOR EACH ROW BEGIN
DECLARE exist_plate CHAR(10);
DECLARE rowcount INT;
SELECT COUNT(*), plate INTO rowcount, exist_plate FROM lp_upload WHERE platematch(NEW.plate, plate) = 1;
IF (1 = rowcount) AND (exist_plate IS NOT NULL) THEN
SET NEW.plate = exist_plate;
END IF;
END
//
DELIMITER ;
DELIMITER //
CREATE DEFINER = CURRENT_USER
FUNCTION `platematch`(`plate_new` char(10), `plate_exist` char(10))
RETURNS INT
BEGIN
DECLARE myindex INT DEFAULT 0;
DECLARE match_count INT DEFAULT 0;
DECLARE maxlength INT;
SET maxlength = length(plate_new);
for_loop: LOOP
SET myindex = myindex + 1;
IF maxlength < myindex THEN
RETURN 0;
END IF;
IF SUBSTRING(plate_new, myindex, 1) = SUBSTRING(plate_exist, myindex, 1)
THEN
SET match_count = match_count +1;
END IF;
IF match_count >= 6 THEN
RETURN 1;
END IF;
IF SUBSTRING(plate_new, myindex, 1) != SUBSTRING(plate_exist, myindex, 1)
AND SUBSTRING(plate_new, myindex, 1) != '$'
AND SUBSTRING(plate_exist, myindex, 1) != '$'
THEN
RETURN 0;
END IF;
END LOOP for_loop;
RETURN 0;
END
//
DELIMITER ;
In the scenario I described above, abcd$1234 will be inserted into the database as-is instead of just being matched to one of multiple potential results automatically.