I'm using in my database, many fields of a certain range, like:
CREATE TABLE figures (
deg FLOAT,-- between 0 and pi
prc FLOAT,-- between 0 and 1
.......
);
CREATE TRIGGER filter1 BEFORE UPDATE ON figures FOR EACH ROW SET
NEW.deg=IF(NEW.deg>3.1415926 OR NEW.deg<0, OLD.deg,NEW.deg),
NEW.prc=IF(NEW.prc>1 OR NEW.prc<0, OLD.prc,NEW.prc),
..........;
CREATE TRIGGER filter2 BEFORE INSERT ON figures FOR EACH ROW SET
NEW.deg=IF(NEW.deg>3.1415926 OR NEW.deg<0, NULL,NEW.deg),
NEW.prc=IF(NEW.prc>1 OR NEW.prc<0, NULL,NEW.prc),
.........;
Is there any way to write it more clearly ?
Something like:
--CREATE PROCEDURE/FUNCTION between()..................
CREATE TABLE figures (
deg FLOAT between(0,3.1415),
prc FLOAT between(0,1),
.......
At least, I don't want to write every filter twice. (ON INSERT,ON UPDATE)
prior to MySQL 8.0.16
Triggers are the best solution
Re:check constraints...
'The CHECK clause is parsed but ignored by all storage engines.'.....
'The reason for accepting but ignoring syntax clauses is for compatibility, to
make it easier to port code from other SQL servers, and to run applications
that create tables with references. '
lifted directly from: http://dev.mysql.com/doc/refman/5.1/en/alter-table.html
From MySQL 8.0.16 though they now work as you would expect
CREATE TABLE figures (
deg FLOAT,
prc FLOAT,
CONSTRAINT `deg_min` CHECK ((`deg` > 0)),
CONSTRAINT `deg_max` CHECK ((`deg` < 3.1415)),
CONSTRAINT `prc_min` CHECK ((`prc` > 0)),
CONSTRAINT `prc_max` CHECK ((`prc` < 1))
)
At least, I don't want to write every filter twice. (ON INSERT,ON UPDATE)
You can write a stored function and call that in your trigger.
DELIMITER $$
CREATE FUNCTION check_deg (degree FLOAT, olddegree FLOAT) RETURNS FLOAT
BEGIN
DECLARE result FLOAT;
result = IF(degree>3.1415926 OR degree <0, olddegree,degree);
RETURN result;
END$$
DELIMITER ;
That way you have one point where the limits are defined and if anything changes you only have to change the boundaries in one place.
The best solution is to use a CHECK() constraint, but MySQL doesn't support CHECK() constraints. (MySQL parses them, then ignores them.)
In some cases, you can replace a CHECK() constraint with a foreign key reference to a table that contains all the valid values. Floating-point numbers are not a good candidate for that kind of solution, though.
That leaves triggers. In your case, your best bet is to use a trigger that calls a stored function.
I have a unfinished idea :
CREATE PROCEDURE check_constraint (table VARCHAR,field VARCHAR,condition VARCHAR)
BEGIN
SET #update_trigger = CONCAT ("
IF EXIST TRIGGER check_constraint_",table,"_",field,"
BEGIN /*I don't know yet what to do*/ END
ELSE /*IF TRIGGER DONT EXIST*/
BEGIN
CREATE TRIGGER check_constraint_",table,"_",field,"
BEFORE UPDATE ON ",table," FOR EACH ROW SET
NEW.",field,"=IF("condition, ", OLD.",field,",NEW.",field,");
END
");
PREPARE update_trigger FROM #update_trigger;
EXECUTE update_trigger;
DEALLOCATE PREPARE update_trigger;
SET #insert_trigger = ..............................
END
After we have a completed function, we can just call it during creation of the database:
CALL check_constraint("table","field","NEW.field<34567");
Related
I want to write trigger for restricting user to insert numeric values and special characters in Name field of column.
CREATE TRIGGER trig_check BEFORE
INSERT ON tempuser
FOR EACH ROW
BEGIN
IF :new.firstname NOT LIKE '%[0-9]%'
THEN
dbms_output.put_line('INSERT ONLY ALPHABETS');
END IF;
END;
/
As #a_horse_with_no_name pointed out, better to add a check constraint like the one below :
alter table TAB
add constraint CHK_NAME_WITHOUT_NUMBER
check (not regexp_like(name,'[0-9]+'));
of course after clearing the data which contain numbers. To accomplish this aim, the following DML statement may be used before the above DDL :
update tab
set name = regexp_replace(name,'[0-9]+','');
create trigger cal_retweet before insert on T
for each row begin
set NEW.retweet_change = NEW.retweet_count - retweet_count where id_str = NEW.id_str
end
SQL said there is syntax error near "where id_str = NEW.id_str"
My table looks like this. Where id_str is a unique identifier for a specific tweet. Since I am inserting 50 tweets from a single user every minute, there would be many same id_str. What I want to look at is the change of retweet_count every minute. tweeted_at is when the user tweeted, created_at is when this data is inserted into my database. I want to generate retweet_change for each new data inserted into the database compared to the same old tweet (into the column retweet_change). How should I write the trigger?
After reading some of your comments I changed my code to :
create trigger cal_retweet before update on T
for each row
begin
set NEW.retweet_change = NEW.retweet_count - OLD.retweet_count;
end;
There is still syntax error
There are several issues with this trigger.
You have some syntax errors. You need proper semicolons to delimit your statements.
You have a WHERE statement that is out of place (and actually not needed). You are acting on only a single row at a time, you don't have to match on the id_str.
In order to factor in a calculation using an existing value from the row, you need access to the OLD keyword. For that, you need a trigger that happens on UPDATE, not INSERT. On INSERT, the retweet_change is simply the same as retweet_count; you could alter your INSERT statement to fix that problem.
You may need to explicitly add a statement delimiter as per the comments below.
So all together, I think this trigger should look like:
DELIMITER //
CREATE TRIGGER cal_retweet BEFORE UPDATE ON T
FOR EACH ROW
BEGIN
SET NEW.retweet_change = NEW.retweet_count - OLD.retweet_count;
END;//
DELIMITER ;
As illustrated in the image below, I want the column grand_score to be automatically calculated when values for paper_score and final_score are inserted.
This is what i have tried:
DELIMITER $$
CREATE TRIGGER myTableAutoSum
BEFORE INSERT ON `stumgr_scores` FOR EACH ROW
BEGIN
SET NEW.grand_score= NEW.paper_score + NEW.final_score;
END;
$$
DELIMITER ;
Issue
when i input values for paper_score and final_score, the table does not get updated (i mean grand_score)
Image
I don't immediately see what is wrong with your trigger, unless one of the values happens to be NULL. That is easily fixed using COALESCE():
SET NEW.grand_score = COALESCE(NEW.paper_score, 0) + COALESCE(NEW.final_score, 0);
I question the use of triggers for this purpose. You also need a trigger for UPDATE, in case the values change that way.
Isn't simpler to remove grand_total from the table and use a view?
create view v_stumgr_scores as
select ss.*,
COALESCE(ss.paper_score, 0) + COALESCE(ss.final_score, 0) as grand_score
from stumgr_scores ss;
Or, better yet, in the most recent versions of MySQL, just use a generated column. That is the best solution -- it even allows you to create an index on the value.
I have written one stored procedure in mysql database as follows :
CREATE PROCEDURE sp_Test(
IN Mode VARCHAR(50),
IN UserID INT,
....
....
)
BEGIN
SET #sqlQuery = "";
IF (Mode = 'Select') THEN
//Select query
ELSEIF (Mode = 'Update') THEN
//update query
ELSEIF (Mode = 'Delete') THEN
//Delete query
END
END
and as per my need I can call it like this
CALL sp_Test("Select", 1, ...)
OR
CALL sp_Test("Update", 1, ...)
OR
CALL sp_Test("Delete", 1, ...)
It is ok for performance? Or I should write individual stored procedures for each mode like
for SELECT
CREATE PROCEDURE sp_Test_Select(
IN UserID INT,
....
....
)
BEGIN
//Select query
END
for UPDATE
CREATE PROCEDURE sp_Test_Update(
IN UserID INT,
....
....
)
BEGIN
//Select query
END
In my opinion, there is no significant difference in the performance of these procedures (unless you're going to use them hundreds of times per second 24x7).
But there is a difference in readability. It's a significant difference. test_DELETE(item) is easier to understand than test("delete", item). Keep in mind that you're writing code not just for the machine, but for the next person who will work on your project. Use your second alternative.
Apart from performance restriction (if any at all); It's always recommended to have separate procedure defined for separate operations (select/insert/update/delete) because
It increases Redability.
Maintainability. separate procedure can be maintained well if at all any changes needed to be done in future.
Consider, about passing parameter for different operations. For select most probably you don't need to pass any parameter to the procedure but for insert, update and delete you will have to pass parameter. Will not be a mess for you to differentiate them within single procedure body?
I'm a SQL noob, and I need a little bit of help understanding the big picture of if it is possible, and if so how to go about filtering a result set based on the return value of a function which is passed one of the fields of the record.
Let's say I have a table called "Numbers" with just one field: "Value".
How could I correctly specify the following "pseudo-sql"?:
SELECT Value FROM numbers WHERE IsPrime(Value)=true
Can I accomplish such a thing, and if so, where/how do I put/store "IsPrime"?
I'm using MySQL.
I agree with extraneon that it's usually better to store this value in the database rather than compute it in the where clause. This way you can compute it once per row and index the data for faster performance.
As long as you are on MySQL 5.x, I would recommend a stored function, as opposed to a UDF. You can add an IS_PRIME column of type TINYINT to your DB, add an index on that column, then use the stored function to calculate the value at insert time. You can even calculate the IS_PRIME value using a before insert trigger if you don't want to change the insert code.
The stored function would look something like this:
DELIMITER $$
DROP FUNCTION IF EXISTS IS_PRIME $$
CREATE FUNCTION IS_PRIME(P_INT BIGINT) RETURNS TINYINT
BEGIN
DECLARE V_FACTOR BIGINT;
DECLARE V_MAX_FACTOR BIGINT;
SET V_FACTOR := 2;
SET V_MAX_FACTOR := round(sqrt(P_INT),0);
WHILE (V_FACTOR <= V_MAX_FACTOR)
DO
IF (P_INT % V_FACTOR) = 0
THEN
RETURN FALSE;
END IF;
SET V_FACTOR := V_FACTOR + 1;
END WHILE;
RETURN TRUE;
END $$
DELIMITER ;
I think you may find some help with the doc about the user-defined functions: http://dev.mysql.com/doc/refman/5.1/en/adding-functions.html
I don't know anything about user defined functions, but I could imagine that for computation-intensive functions it might be best to have that value precomputed and stored in the database somewhere.
Depending on how the data gets in the database you could require the client to compute isPrime (that can be done if the client is a web service or app on your own server), or perhaps a scheduled job which processes every record with isPrime is null or something like that. That is not instantaneous, but for some scenario's it may be good enough.
If isPrime is only used sometimes you could also post-process/filter the data on the client when retrieving data.