MySQL Trigger to Auto-Update (or insert) Column Field - mysql

I have an SQL query I can run that looks like:
SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX(name, ' ', 1), ' ', -1) AS first_name,
If( length(name) - length(replace(name, ' ', ''))>1,
SUBSTRING_INDEX(SUBSTRING_INDEX(name, ' ', 2), ' ', -1) ,NULL)
as middle_name,
SUBSTRING_INDEX(SUBSTRING_INDEX(name, ' ', 3), ' ', -1) AS last_name
FROM people
While this is works great to split up the full name into first_name, middle_name, and last_name I need a way to have this done automatically; can't go in and run the query and manually update the table each time a person is added.
I've tried to play around with this query in the form of a trigger but keep getting errors, generally the error states "Not allowed to return a result set from trigger"
Any help getting this working would be great

In a BEFORE INSERT trigger, use the qualifier NEW. to reference values assigned to columns of the row being inserted. For example, NEW.col would reference the value supplied for the column col.
Assign a value (or expression) to NEW.col to replace the value supplied for column col, and the value assigned will be inserted instead.
A trigger to accomplish something similar to SELECT statement would look something like this:
DELIMITER $$
CREATE TRIGGER mytrigger
BEFORE INSERT ON mytable
FOR EACH ROW
BEGIN
SET NEW.first_name = SUBSTRING_INDEX(SUBSTRING_INDEX( NEW.name, ' ', 1), ' ', -1);
SET NEW.last_name = SUBSTRING_INDEX(SUBSTRING_INDEX( NEW.name, ' ', 3), ' ', -1);
SET NEW.middle_name = IF(LENGTH( NEW.name) - LENGTH(REPLACE( NEW.name, ' ', ''))>1
,SUBSTRING_INDEX(SUBSTRING_INDEX( NEW.name, ' ', 2), ' ', -1)
,NULL);
END$$
DELIMITER ;
The error you are getting:
Error: 1415 SQLSTATE: 0A000 (ER_SP_NO_RETSET)
Message: Not allowed to return a result set from a %s
Is due to a documented restriction that applies to both FUNCTION and TRIGGER. (This restriction also applies to a PROCEDURE that is called from the context of a FUNCTION or TRIGGER.)

Related

Calculation within a string field MySQL

I have been attempting to edit/add a value string column (process) within a table.
The current values in the string is as follow:
1:38,25:39,41:101
What I want to do is add 1000 to every value after "X:" so after the query the values should read:
1:1038,25:1039,41:1101
I have looked at CONCAT but that seems to only insert a value into a string within certain parameters. Any ideas?
You can use CAST CONCAT and SUBSTRING_INDEX functions to get the required output, e.g.:
SELECT
CONCAT(SUBSTRING_INDEX(value, ':', 1), ":", (CAST(SUBSTRING_INDEX(value, ':', -1) AS UNSIGNED) + 1000))
FROM test;
Here's the SQL Fiddle.
Use of variables can help you achieve what you want:
select #pre := SUBSTRING_INDEX(SUBSTRING_INDEX(`column_name`, ':', 1), '0', -1),
#post := SUBSTRING_INDEX(SUBSTRING_INDEX(`column_name`, ':', 2), '0', -1),
concat(#pre,":",#post+1000) as required_value from table_name;
References:
for use of variables:
https://dev.mysql.com/doc/refman/5.7/en/user-variables.html
for substring_index:
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring-index
You should normalize the data and store it in a separate table. That said, to answer your question you could use these queries to achieve what you want:
CREATE TEMPORARY TABLE temp (val VARCHAR(50));
SET #str = CONCAT("INSERT INTO temp (val) VALUES ('",REPLACE((SELECT org FROM mytable LIMIT 1), ",", "'),('"),"');");
PREPARE st FROM #str;
EXECUTE st;
SELECT
GROUP_CONCAT(DISTINCT CONCAT(SUBSTRING_INDEX(val, ':', 1), ":", (CAST(SUBSTRING_INDEX(val, ':', -1) AS UNSIGNED) + 1000)))
FROM temp;
Just make sure to replace SELECT org FROM mytable LIMIT 1 in the above query with your query that returns the string 1:38,25:39,41:101 you need to edit. Be aware that this example only shows how to process the value in one string. If you need to process values of multiple rows, you need to adjust a bit more...
Check sqlfiddle: http://sqlfiddle.com/#!9/bf6da4/1/0

combine two before insert on triggers mysql

I have a pair of triggers on one table that concat 3 columns into one. I need to do this same thing with different formatting into another column. Mysql tells me i cant have the same trigger at the same time. Is there a way to combine the two sets of triggers so that they are each calling two things? I have looked for this and cant seem to find an answer or figure it out. Just adding the SET call into it throws the error. Thanks
Here are the current working triggers if they are seperated.
Set 1
CREATE TRIGGER combinedDescNameinsert_trigger
BEFORE INSERT ON markers
FOR EACH ROW
SET new.combinedDescName = CONCAT(new.descName, '-', new.DoorGroupID, '-', new.DoorID);
CREATE TRIGGER combinedDescNameupdate_trigger
BEFORE UPDATE ON markers
FOR EACH ROW
SET new.combinedDescName = CONCAT(new.descName, '-', new.DoorGroupID, '-', new.DoorID);
Set 2
CREATE TRIGGER combinedDoorNameinsert_trigger
BEFORE INSERT ON markers
FOR EACH ROW
SET new.combinedDoorName = CONCAT(new.descName, ' - DoorGroupID: ', new.DoorGroupID, ' - DoorID: ', new.DoorID);
CREATE TRIGGER combinedDoorNameupdate_trigger
BEFORE UPDATE ON markers
FOR EACH ROW
SET new.combinedDoorName = CONCAT(new.descName, ' - DoorGroupID: ', new.DoorGroupID, ' - DoorID: ', new.DoorID);
Just like, IF...THEN, LOOP, stored procedures, etc... triggers with multiple statements must enclose them in BEGIN...END. Since such statements need terminated themselves, the delimiter must be temporarily changed to differential between body statements' ends and the end of the trigger itself.
DELIMITER $$
CREATE TRIGGER combinedDescNameinsert_trigger
BEFORE INSERT ON markers
BEGIN
FOR EACH ROW
SET new.combinedDescName = CONCAT(new.descName, '-', new.DoorGroupID, '-', new.DoorID);
SET new.combinedDoorName = CONCAT(new.descName, ' - DoorGroupID: ', new.DoorGroupID, ' - DoorID: ', new.DoorID);
END$$
CREATE TRIGGER combinedDescNameupdate_trigger
BEFORE UPDATE ON markers
FOR EACH ROW
BEGIN
SET new.combinedDescName = CONCAT(new.descName, '-', new.DoorGroupID, '-', new.DoorID);
SET new.combinedDoorName = CONCAT(new.descName, ' - DoorGroupID: ', new.DoorGroupID, ' - DoorID: ', new.DoorID);
END$$
DELIMITER ;

MySql replacing spaces with hyphens

A day ago, I asked this question on stackoverflow. Sure, that works well, but does anyone know how I can do the same thing in a MySql statement without any php involved?
Eg: select preg_replace(:songName,' ', '-') //Ugh, this is wrong.
What I'm trying to do Is replace spaces with a -. But sometimes, when there is a space, I'll get more -
Eg: Metallica - Hero of the Day ends up as Metallica---Hero-of-the-Day
Any chance of making it just: Metallica-Hero-of-the-Day
BTW: It's not only song names I'm replacing.
I'm ok with a simple MySql replace, but I can see doing the above is going to need more than that.
I would replace spaces with hyphens first, then deal with any multiple hyphens that may have been created:
select replace(replace(replace(songTitle, ' ', '-'), '---', '-'), '--', '-')
I've replaced --- and -- separately because there are edge cases which overall would require both, and in that order.
See SQLFiddle
Use a user defined function like this(use delimetres accordingly)
CREATE FUNCTION replace_spaceWithHyphen(textToReplace varchar(100))
RETURNS TEXT
BEGIN
DECLARE occHyphen int;
DECLARE occSpace int;
set occHyphen = 1;
set occSpace = 1;
WHILE (occHyphen <> 0 || occSpace <> 0) DO
SELECT LOCATE('--',textToReplace) into occHyphen;
SELECT LOCATE(' ',textToReplace) into occSpace;
SELECT REPLACE(textToReplace,' ','-') into textToReplace;
SELECT REPLACE(textToReplace,'--','-') into textToReplace;
END WHILE;
RETURN textToReplace;
END;
Then call your select like this:
SELECT replace_spaceWithHyphen('Metallica - Hero of the Day');
Answer would be:
TEXT
Metallica-Hero-of-the-Day
SAMPLE FIDDLE
This should work:
select
replace(
replace(
replace('Metallica - Hero of the Day', '-', ' ')
, ' ', '')
, ' ', '-')
You may write your query. It is so easy for you.
Suppose you have a table named class(id, classname) with two fields. Now you insert in your table in classname field i.e. Metallica - Hero of the Day.
Now you can execute this by below given program.
mysql_connect('localhost','root','');
mysql_select_db('dbname'); // Please insert your dbname
$query = mysql_query('SELECT classname, REPLACE(classname," ","-") from class');
$record = mysql_fetch_array($query);
echo $record['REPLACE(classname," ","-")'];
It will give output. i.e. Metallica---Hero-of-the-Day.
and if you replace your query with. I have taken help from Bohemian answer for below query.
$query = mysql_query("SELECT classname, replace(replace(replace(classname, ' ', '-'), '---', '-'), '--', '') from class");
$record = mysql_fetch_array($query);
echo $record["replace(replace(replace(classname, ' ', '-'), '---', '-'), '--', '')"];
You will get result i.e. Metallica-Hero-of-the-Day
That's it. Easy.
Thanks
You can try this method.
UPDATE TABLE_NAME SET column_name = REPLACE(column_name, old_value, new_value);
For Example
UPDATE TABLENAME SET column_name = REPLACE(column_name, '-', ' ');
Hope this will helps you.

INSERT INTO mySQL procedure

INSERT INTO in MySQL>
this is a part of code a procedure:
if ParentList is null then
set #sql=concat('insert into ', table_name, tabSave, '(md5Id, CommentsId, Parent, Content, UserId, Title, Nick) ',
'values(', #md5Id, ', ', CommentsId, ', null, \'', Content, '\'' ,UserId,',',Title,',',Nick, ')');
else
set #sql=concat('insert into ', table_name, tabSave, '(md5Id, CommentsId, Parent, Content, UserId, Title, Nick) ',
'values(', #md5Id, ', ', CommentsId, ', ', #Parent, ', \'', Content, '\ ,', UserId,', ', Title,', ', Nick, ')');
end if;
This character sign ' is badly inserted and I dont know where :
My error:
CALL AddDynamicTable3('asd65xb', 'xxx', 5, NULL, 'MojWstep', 22, 'New title', 'Ethan') Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '22,New title,Przemo)' at line 1
XXX - table_name
other variables are not significant example tabSave, table_name ...
Earlier I wrote the same procedure but without three variables UserId, Title, Nick and its OK but when I put this variables I only see ERROR.
See if this works:
'\ ,', UserId,',
from the second query, change to
'\ ', UserId,',

Split value from one field to two

I've got a table field membername which contains both the last name and the first name of users. Is it possible to split those into 2 fields memberfirst, memberlast?
All the records have this format "Firstname Lastname" (without quotes and a space in between).
Unfortunately MySQL does not feature a split string function. However you can create a user defined function for this, such as the one described in the following article:
MySQL Split String Function by Federico Cargnelutti
With that function:
DELIMITER $$
CREATE FUNCTION SPLIT_STR(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
)
RETURNS VARCHAR(255) DETERMINISTIC
BEGIN
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '');
END$$
DELIMITER ;
you would be able to build your query as follows:
SELECT SPLIT_STR(membername, ' ', 1) as memberfirst,
SPLIT_STR(membername, ' ', 2) as memberlast
FROM users;
If you prefer not to use a user defined function and you do not mind the query to be a bit more verbose, you can also do the following:
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(membername, ' ', 1), ' ', -1) as memberfirst,
SUBSTRING_INDEX(SUBSTRING_INDEX(membername, ' ', 2), ' ', -1) as memberlast
FROM users;
SELECT variant (not creating a user defined function):
SELECT IF(
LOCATE(' ', `membername`) > 0,
SUBSTRING(`membername`, 1, LOCATE(' ', `membername`) - 1),
`membername`
) AS memberfirst,
IF(
LOCATE(' ', `membername`) > 0,
SUBSTRING(`membername`, LOCATE(' ', `membername`) + 1),
NULL
) AS memberlast
FROM `user`;
This approach also takes care of:
membername values without a space: it will add the whole string to memberfirst and sets memberlast to NULL.
membername values that have multiple spaces: it will add everything before the first space to memberfirst and the remainder (including additional spaces) to memberlast.
The UPDATE version would be:
UPDATE `user` SET
`memberfirst` = IF(
LOCATE(' ', `membername`) > 0,
SUBSTRING(`membername`, 1, LOCATE(' ', `membername`) - 1),
`membername`
),
`memberlast` = IF(
LOCATE(' ', `membername`) > 0,
SUBSTRING(`membername`, LOCATE(' ', `membername`) + 1),
NULL
);
It seems that existing responses are over complicated or not a strict answer to the particular question.
I think, the simple answer is the following query:
SELECT
SUBSTRING_INDEX(`membername`, ' ', 1) AS `memberfirst`,
SUBSTRING_INDEX(`membername`, ' ', -1) AS `memberlast`
;
I think it is not necessary to deal with more-than-two-word names in this particular situation. If you want to do it properly, splitting can be very hard or even impossible in some cases:
Johann Sebastian Bach
Johann Wolfgang von Goethe
Edgar Allan Poe
Jakob Ludwig Felix Mendelssohn-Bartholdy
Petőfi Sándor
Virág Vendelné Farkas Margit
黒澤 明
In a properly designed database, human names should be stored both in parts and in whole. This is not always possible, of course.
If your plan is to do this as part of a query, please don't do that (a). Seriously, it's a performance killer. There may be situations where you don't care about performance (such as one-off migration jobs to split the fields allowing better performance in future) but, if you're doing this regularly for anything other than a mickey-mouse database, you're wasting resources.
If you ever find yourself having to process only part of a column in some way, your DB design is flawed. It may well work okay on a home address book or recipe application or any of myriad other small databases but it will not be scalable to "real" systems.
Store the components of the name in separate columns. It's almost invariably a lot faster to join columns together with a simple concatenation (when you need the full name) than it is to split them apart with a character search.
If, for some reason you cannot split the field, at least put in the extra columns and use an insert/update trigger to populate them. While not 3NF, this will guarantee that the data is still consistent and will massively speed up your queries. You could also ensure that the extra columns are lower-cased (and indexed if you're searching on them) at the same time so as to not have to fiddle around with case issues.
And, if you cannot even add the columns and triggers, be aware (and make your client aware, if it's for a client) that it is not scalable.
(a) Of course, if your intent is to use this query to fix the schema so that the names are placed into separate columns in the table rather than the query, I'd consider that to be a valid use. But I reiterate, doing it in the query is not really a good idea.
use this
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX( `membername` , ' ', 2 ),' ',1) AS b,
SUBSTRING_INDEX(SUBSTRING_INDEX( `membername` , ' ', -1 ),' ',2) AS c FROM `users` WHERE `userid`='1'
In MySQL this is working this option:
SELECT Substring(nameandsurname, 1, Locate(' ', nameandsurname) - 1) AS
firstname,
Substring(nameandsurname, Locate(' ', nameandsurname) + 1) AS lastname
FROM emp
Not exactly answering the question, but faced with the same problem I ended up doing this:
UPDATE people_exit SET last_name = SUBSTRING_INDEX(fullname,' ',-1)
UPDATE people_exit SET middle_name = TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(fullname,last_name,1),' ',-2))
UPDATE people_exit SET middle_name = '' WHERE CHAR_LENGTH(middle_name)>3
UPDATE people_exit SET first_name = SUBSTRING_INDEX(fullname,concat(middle_name,' ',last_name),1)
UPDATE people_exit SET first_name = middle_name WHERE first_name = ''
UPDATE people_exit SET middle_name = '' WHERE first_name = middle_name
The only case where you may want such a function is an UPDATE query which will alter your table to store Firstname and Lastname into separate fields.
Database design must follow certain rules, and Database Normalization is among most important ones
I had a column where the first and last name were both were in one column. The first and last name were separated by a comma. The code below worked. There is NO error checking/correction. Just a dumb split. Used phpMyAdmin to execute the SQL statement.
UPDATE tblAuthorList SET AuthorFirst = SUBSTRING_INDEX(AuthorLast,',',-1) , AuthorLast = SUBSTRING_INDEX(AuthorLast,',',1);
13.2.10 UPDATE Syntax
This takes smhg from here and curt's from Last index of a given substring in MySQL and combines them. This is for mysql, all I needed was to get a decent split of name to first_name last_name with the last name a single word, the first name everything before that single word, where the name could be null, 1 word, 2 words, or more than 2 words. Ie: Null; Mary; Mary Smith; Mary A. Smith; Mary Sue Ellen Smith;
So if name is one word or null, last_name is null. If name is > 1 word, last_name is last word, and first_name all words before last word.
Note that I've already trimmed off stuff like Joe Smith Jr. ; Joe Smith Esq. and so on, manually, which was painful, of course, but it was small enough to do that, so you want to make sure to really look at the data in the name field before deciding which method to use.
Note that this also trims the outcome, so you don't end up with spaces in front of or after the names.
I'm just posting this for others who might google their way here looking for what I needed. This works, of course, test it with the select first.
It's a one time thing, so I don't care about efficiency.
SELECT TRIM(
IF(
LOCATE(' ', `name`) > 0,
LEFT(`name`, LENGTH(`name`) - LOCATE(' ', REVERSE(`name`))),
`name`
)
) AS first_name,
TRIM(
IF(
LOCATE(' ', `name`) > 0,
SUBSTRING_INDEX(`name`, ' ', -1) ,
NULL
)
) AS last_name
FROM `users`;
UPDATE `users` SET
`first_name` = TRIM(
IF(
LOCATE(' ', `name`) > 0,
LEFT(`name`, LENGTH(`name`) - LOCATE(' ', REVERSE(`name`))),
`name`
)
),
`last_name` = TRIM(
IF(
LOCATE(' ', `name`) > 0,
SUBSTRING_INDEX(`name`, ' ', -1) ,
NULL
)
);
Method I used to split first_name into first_name and last_name when the data arrived all in the first_name field. This will put only the last word in the last name field, so "john phillips sousa" will be "john phillips" first name and "sousa" last name. It also avoids overwriting any records that have been fixed already.
set last_name=trim(SUBSTRING_INDEX(first_name, ' ', -1)), first_name=trim(SUBSTRING(first_name,1,length(first_name) - length(SUBSTRING_INDEX(first_name, ' ', -1)))) where list_id='$List_ID' and length(first_name)>0 and length(trim(last_name))=0
UPDATE `salary_generation_tbl` SET
`modified_by` = IF(
LOCATE('$', `other_salary_string`) > 0,
SUBSTRING(`other_salary_string`, 1, LOCATE('$', `other_salary_string`) - 1),
`other_salary_string`
),
`other_salary` = IF(
LOCATE('$', `other_salary_string`) > 0,
SUBSTRING(`other_salary_string`, LOCATE('$', `other_salary_string`) + 1),
NULL
);
In case someone needs to run over a table and split a field:
First we use the function mention above:
CREATE DEFINER=`root`#`localhost` FUNCTION `fn_split_str`($str VARCHAR(800), $delimiter VARCHAR(12), $position INT) RETURNS varchar(800) CHARSET utf8
DETERMINISTIC
BEGIN
RETURN REPLACE(
SUBSTRING(
SUBSTRING_INDEX($str, $delimiter, $position),
LENGTH(
SUBSTRING_INDEX($str, $delimiter, $position -1)
) + 1
),
$delimiter, '');
END
Second, we run in a while loop on the string until there isn't any results (I've added $id for JOIN clause):
CREATE DEFINER=`root`#`localhost` FUNCTION `fn_split_str_to_rows`($id INT, $str VARCHAR(800), $delimiter VARCHAR(12), $empty_table BIT) RETURNS int(11)
BEGIN
DECLARE position INT;
DECLARE val VARCHAR(800);
SET position = 1;
IF $empty_table THEN
DROP TEMPORARY TABLE IF EXISTS tmp_rows;
END IF;
SET val = fn_split_str($str, ',', position);
CREATE TEMPORARY TABLE IF NOT EXISTS tmp_rows AS (SELECT $id as id, val as val where 1 = 2);
WHILE (val IS NOT NULL and val != '') DO
INSERT INTO tmp_rows
SELECT $id, val;
SET position = position + 1;
SET val = fn_split_str($str, ',', position);
END WHILE;
RETURN position - 1;
END
Finally we can use it like that:
DROP TEMPORARY TABLE IF EXISTS tmp_rows;
SELECT SUM(fn_split_str_to_rows(ID, FieldToSplit, ',', 0))
FROM MyTable;
SELECT * FROM tmp_rows;
You can use the id to join to other table.
In case you are only splitting one value you can use it like that
SELECT fn_split_str_to_rows(null, 'AAA,BBB,CCC,DDD,EEE,FFF,GGG', ',', 1);
SELECT * FROM tmp_rows;
We don't need to empty the temporary table, the function will take care of that.
mysql 5.4 provides a native split function:
SPLIT_STR(<column>, '<delimiter>', <index>)