MySQL trigger: convert text to numbers - php

I have a table with these fields:
users {id,name,week1,week2,week3,overall_score}
The weeks are TEXT and the overall_score is an INT
In week one either Absent or Present is put into it. Absent is 0 points and Present is 10. I would like to create a trigger to run automatically after each update to calculate the overall score and convert the text to a number.

I would go about solving this problem using the following 2 steps:
Create a function to determine the score for a particular week...
DELIMITER //
CREATE FUNCTION `GetWeekScore`(`WeekAttendance` TEXT) RETURNS INT
BEGIN
IF WeekAttendance = 'Present' THEN
RETURN 10;
ELSE
RETURN 0;
END IF;
END//
DELIMITER ;
Use that function in a trigger...
DELIMITER //
CREATE TRIGGER `CalculateOverallScore` BEFORE UPDATE ON `users`
FOR EACH ROW
BEGIN
SET NEW.overall_score = (GetWeekScore(NEW.week1) +
GetWeekScore(NEW.week2) +
GetWeekScore(NEW.week3));
END//
DELIMITER ;

Related

Sql correct filter for floats values in string column

I have table invoices and there is column 'total' varchar(255). There are values like these: "500.00", "5'199.00", "129.60", "1.00" and others.
I need select records and filter by total column. For example, find records where total is not more than 180.
I tried this:
SELECT total from invoices WHERE invoices.total <= '180'
But in result there are :
125.25
100.50
1593.55 - not correct
4'799.00 - not correct
1.00
-99.00
2406.52 -not correct
How can I fix it and write correct filter for this column? Thanks!
You can use cast() function to convert it in float
SELECT total from invoices WHERE cast(invoices.total as decimal(16,2)) <= 180
Why are you storing numbers as strings? That is a fundamental problem with your data model, and you should fix it.
Sometimes, we are stuck with other people's really, really, really bad decisions. If that is the case, you can attempt to solve this with explicit conversion:
SELECT i.total
FROM invoices i
WHERE CAST(REPLACE(i.total, '''', '') as DECIMAL(20, 4)) <= 180;
Note that this will return an error if you have other unexpected characters in your totals.
If the string starts with a number, then contains non-numeric characters, you can use the CAST() function or convert it to a numeric implicitly by adding a 0:
SELECT CAST('1234abc' AS UNSIGNED); -- 1234
SELECT '1234abc'+0; -- 1234
To extract numbers out of an arbitrary string you could add a custom function like this:
DELIMITER $$
CREATE FUNCTION `ExtractNumber`(in_string VARCHAR(50))
RETURNS INT
NO SQL
BEGIN
DECLARE ctrNumber VARCHAR(50);
DECLARE finNumber VARCHAR(50) DEFAULT '';
DECLARE sChar VARCHAR(1);
DECLARE inti INTEGER DEFAULT 1;
IF LENGTH(in_string) > 0 THEN
WHILE(inti <= LENGTH(in_string)) DO
SET sChar = SUBSTRING(in_string, inti, 1);
SET ctrNumber = FIND_IN_SET(sChar, '0,1,2,3,4,5,6,7,8,9');
IF ctrNumber > 0 THEN
SET finNumber = CONCAT(finNumber, sChar);
END IF;
SET inti = inti + 1;
END WHILE;
RETURN CAST(finNumber AS UNSIGNED);
ELSE
RETURN 0;
END IF;
END$$
DELIMITER ;
Once the function is defined, you can use it in your query:
SELECT total from invoices WHERE ExtractNumber(invoices.total) <= 180

how to update multiple columns from a trigger MySQL

hi i am using mysql trigger to update a table on another table's insertion
this trigger works fine
CREATE TRIGGER `update_pupil_subject` AFTER INSERT ON `pupil_marks`
FOR EACH ROW
BEGIN
UPDATE pupil_subjects SET NumberOfStudens = NumberOfStudens + 1 WHERE NEW.SubjectID = SubjectID;
END$$
but this gives an error
CREATE TRIGGER `update_pupil_subject` AFTER INSERT ON `pupil_marks`
FOR EACH ROW
BEGIN
UPDATE pupil_subjects SET NumberOfStudens = NumberOfStudens + 1 , AverageMarks = (SELECT AVG(Marks) FROM pupil_marks WHERE NEW.StudentID = StudentID ) WHERE NEW.SubjectID = SubjectID;
END$$
how to write this correctly , please help . thanks in advance .
Apparently there were problems when sub-queries were used:
Can you try splitting the SQL statement:
DELIMITER $$
CREATE TRIGGER `update_pupil_subject`
AFTER INSERT
ON `pupil_marks`
FOR EACH ROW
BEGIN
DECLARE avg_marks float;
SELECT AVG(Marks)
INTO avg_marks
FROM pupil_marks
WHERE NEW.SubjectID = SubjectID;
UPDATE pupil_subjects
SET NumberOfStudens = NumberOfStudens + 1, AverageMarks = avg_marks
WHERE NEW.SubjectID = SubjectID;
END
$$
Edit: Use
SHOW TRIGGERS WHERE `table` = 'pupil_marks';
to get all triggers defined on pupil_marks. You can't have multiple triggers on an event as all actions can be covered in single trigger.
NOTE: I think AVG(Marks) is for a given subject, so modified trigger definition accordingly.
declare a variable inside the trigger and assign it with the subquery
declare avg_mark integer default 0;
set avg_mark := (SELECT AVG(Marks) FROM pupil_marks WHERE NEW.StudentID = StudentID);
then use the variable "avg_mark" in your update statement...
it may work...
if not then check the delimiter just below phpmyadmin sql box . It should be "$$"

can soundex be used on portion of a column in mysql?

Is it possible to use soundex to compare portion of a column with search term? For example if a user searches "fiftythree" - it will find "Nirve Sports Fifty-Three Cruiser in Gold Metallic". I tried combining soundex with locate function but got an error. Here is my php code:
$soundex = soundex($keyword);
$soundexPrefix = substr($soundex, 0, 2);
$sql = "SELECT name ".
"FROM products WHERE SOUNDEX(LOCATE('$keyword', name)) LIKE '%$soundexPrefix%'";
You'll need to break up each word and do the SOUNDEX comparison, which is exactly what this function I'm going to tell you about does.
Using the function
Example usage: SELECT p.name FROM products p WHERE soundex_match('fiftythree', p.name, ' ')
It takes 3 arguments:
needle: The word you are looking for
haysack: The string of words among which you are searching
splitChar: The whitespace charater that’ll split the string into
single words. Generally it is the space(‘ ‘)
If any word in haystack sounds similar to needle, the function will return 1 and 0 otherwise.
Creating the function in your database
So go into your database (phpMyAdmin or the command line) and execute this, you only need to do it this one time):
drop function if exists soundex_match;
delimiter $$
create function soundex_match (needle varchar(128), haystack text, splitChar varchar(1)) returns tinyint
deterministic
begin
declare spacePos int;
declare searchLen int default length(haystack);
declare curWord varchar(128) default '';
declare tempStr text default haystack;
declare tmp text default '';
declare soundx1 varchar(64) default soundex(needle);
declare soundx2 varchar(64) default '';
set spacePos = locate(splitChar, tempStr);
while searchLen > 0 do
if spacePos = 0 then
set tmp = tempStr;
select soundex(tmp) into soundx2;
if soundx1 = soundx2 then
return 1;
else
return 0;
end if;
end if;
if spacePos != 0 then
set tmp = substr(tempStr, 1, spacePos-1);
set soundx2 = soundex(tmp);
if soundx1 = soundx2 then
return 1;
end if;
set tempStr = substr(tempStr, spacePos+1);
set searchLen = length(tempStr);
end if;
set spacePos = locate(splitChar, tempStr);
end while;
return 0;
end
$$
delimiter ;
http://www.imranulhoque.com/mysql/mysql-function-soundex-match-multi-word-string/

Get Updated Value in MySQL instead of affected rows

I've been trying to find an answer to this question, but haven't found any definitive "yes" or "no" in all my research.
I'm running a simple MySQL query like this:
UPDATE item SET `score`=`score`+1 WHERE `id`=1
Is there a way for that query to return the updated value, instead of the number of rows affected? Just as a reference, I'm doing this in PHP, so the actual code looks like:
$sql = "UPDATE item SET `score`=`score`+1 WHERE `id`=1";
$new_value = mysql_query($sql);
//Unfortunately this does not return the new value
I know I could do a second query and just SELECT the value, but I'm trying to cut down on queries as much as possible. Is there a way?
You can do it with a stored procedure that updates, and then selects the new value into an output parameter.
The following returns one column new_score with the new value.
DELIMITER $$ -- Change DELIMITER in order to use ; withn the procedure
CREATE PROCEDURE increment_score
(
IN id_in INT
)
BEGIN
UPDATE item SET score = score + 1 WHERE id = id_in;
SELECT score AS new_score FROM item WHERE id = id_in;
END
$$ -- Finish CREATE PROCEDURE statement
DELIMITER ; -- Reset DELIMITER to standard ;
In PHP:
$result = mysql_query("CALL increment_score($id)");
$row = mysql_fetch_array($result);
echo $row['new_score'];
No, there's nothing like postgresql's UPDATE ... RETURNING output_expression in MySQL (yet?).
If you don't want to run another Query SELECT then here is another way to do it. I have modified Mr. Berkowski code for reference:
DELIMITER $$
CREATE PROCEDURE increment_score
(
IN id_in INT
)
BEGIN
set #newScore := null;
UPDATE item SET score = IF((#newScore := score+1) <> NULL IS NULL, #newScore, NULL) WHERE id = id_in;
SELECT #newScore;
END
DELIMITER ;
No you cant. You could make a function or stored procedure that could do the insert and return the updated value but that would still require you to execute two queries from within the function or stored procedure.
You can create a trigger, and you will know everything about the modifications.

Convert tax like IRS function to SQL

Trying to convert this tax-like IRS function Calculating revenue share at different tiers to SQL. The assumption is the table would be one column (price) and the tiers will be added dynamically.
My first attempt has some case statements that didn't work out well. I have since scrapped it :) Thanks for the help!
SQL isn't really suited to iterative loops. I'd
select * from `brackets` where `tier` < $income
From there I'd... well, you linked to some sample code in another language yourself. You should really do it that way.
If you insist otherwise, SQL can do it:
DELIMITER $$
create procedure `calcTax`(IN v_param1 int)
begin
declare v_tier int;
declare v_rate decimal(3,3);
declare v_untaxed int default 0;
declare v_taxTotal float default 0;
set v_untaxed = v_param1;
set v_taxTotal = 0;
while v_untaxed > 0 do
select max(`tier`), max(`rate`) into v_tier, v_rate
from `brackets` where tier < v_untaxed order by `tier` desc limit 1;
set v_taxTotal = v_taxTotal + (v_untaxed - v_tier) * v_rate;
set v_untaxed = v_tier;
end while;
select v_taxTotal;
end;
$$
Correct datatypes are up to you. You'll need a tax brackets table that works out. I made one for testing below (with poorly advised data types).
create table brackets (tier int primary key, rate float);
insert into brackets values (0, 0), (10000, 0.1), (50000, 0.2);
You could create a MYSQL user defined function.
http://dev.mysql.com/doc/refman/5.1/en/adding-functions.html

Categories