I am trying to create a user-defined function in MySQL in order to generate unique numbers. As the auto_increment feature of MySQL just increments its seed by one, I need to have such a function to handle fields that need to be incremented by more than one. Here is my SQL script:
DELIMITER $$
CREATE FUNCTION `getUniqueID`(
id_type CHAR(1)
) RETURNS INT(10)
BEGIN
DECLARE run INT(10);
START TRANSACTION;
SELECT unique_gen_id INTO #run FROM tbl_unique_seed WHERE id_type = #id_type;
UPDATE tbl_unique_seed SET unique_gen_id = (unique_gen_id + 1) WHERE id_type = #id_type;
COMMIT;
RETURN run;
END$$
DELIMITER ;
I do not have much experience in MySQL, but creating this kind of functions in SQL Server is quite easy. It will be nice of you to help me figure out a solution for this issue. Currently, I am facing some syntax errors. The most basic ones relate to the syntax of my transaction and the select statement.
See the docs here: http://dev.mysql.com/doc/refman/5.0/en/replication-options-master.html#sysvar_auto_increment_increment
You can change the value of auto_increment_increment, which is normally 1.
Related
I am writing my first stored procedure as a trigger. I am doing this in a dev migration as we have two systems which don't speak to each other in dev, so I need to mock the data which would normally come from the other system.
My procedure is added as part of our dev migration script.
DELIMITER |;
CREATE TRIGGER `activity_insert` AFTER INSERT ON `activity`
FOR EACH ROW
BEGIN
UPDATE `activity` AS `a` JOIN `handle` AS `h` on `a.handle_id` = `h.handle_id` SET `path` = CONCAT(`h.handle`,'/',`a.activity_handle`) WHERE `a.path` IS NULL;
END;
|
DELIMITER;
I would expect the logic to be:
DELIMITER $$
CREATE TRIGGER activity_insert BEFORE INSERT ON activity
FOR EACH ROW
BEGIN
IF new.path IS NULL THEN
SET new.path = (SELECT CONCAT(h.handle, '/', new.activity_handle)
FROM handle h
WHERE new.handle_id = h.handle_id
);
END IF;
END;$$
DELIMITER;
There are numerous problem with your code:
You don't update the table being modified using update.
You want a "before" triggers, not an "after trigger".
Don't use | for the the delimited. It is a valid MySQL operator.
You have over-used the backtick, including putting the table alias in with the column alias.
This assumes that handle.handle_id is unique. This seems like a reasonable assumption based on the names, but you can add limit 1 to guarantee no more than one row is returned.
I am trying to write a function that looks for a value assigned to its configuration in a parent-child tree, if the value is null or empty it looks one level up for the value.
I am currently getting syntax errors when trying to create the function.
This is what i have so far,
DELIMITER //
CREATE FUNCTION `db`.`Configuration`(
`ColumnName` VARCHAR(128),
`CID` INT
)
RETURNS VARCHAR(256)
NOT DETERMINISTIC
BEGIN
DECLARE Config VARCHAR(256) DEFAULT NULL;
DECLARE Parent INT;
WHILE (#Config IS NULL OR #Config = "") DO
SELECT #ColumnName INTO #Config, `ParentID` INTO #Parent FROM `Table` WHERE `ID`=#CID;
END WHILE;
RETURN CONCAT(#Config, '::', #Parent);
END ;
//
DELIMITER ;
I am getting the following error when I try to add the function:
1327 - Undeclared variable: ParentID
Any help would be greatly appreciated!
You receive the error message in the question because you have multiple into clauses, whereas according to mysql manual on select ... into ... you can only have one. So, to get rid of this specific error message you nee to rewrite your select statement as:
SELECT #ColumnName, `ParentID` INTO #Config, #Parent FROM `Table` WHERE `ID`=#CID;
However, there are some further issues with your code:
varname and #varname do not refer to the same variable. The first one is either a function / stored proc parameter or local variable, while the 2nd one is a user-defined variable. In your code you must remove the # from the variable names.
You cannot use a variable in place of a field name in an sql statement. You must use dynamic sql with prepared statements to achieve this. See the following SO question on how to this: How To have Dynamic SQL in MySQL Stored Procedure
You do not overwrite CID parameter in your while loop. This means that if the first iteration the configuration will remain null, then you have an infinite loop. You should change the value of CID in your loop.
I cannot guarantee that there are no further errors in your code.
There are a few problems with your function:
You are using SELECT...INTO incorrectly. When selecting multiple values you should only use INTO once. For example SELECT a,b into #a,#b FROM...
You are using user-defined variables with similar names to your function parameters, but they are not the same thing. In your code CID and #CID are different. I suggest using standard naming prefixes to clarify this: for example use p_ for function parameters and v_ for local function variables. You shouldn't need to use user-defined variables at all.
Your WHILE loop is bound to lead to infinite loops since the query criteria never changes. If it returns NULL or empty string once, it will keep returning them forever.
Here's a quick rewrite to address the above issues. I'll leave it to you to implement the WHILE loop correctly:
DELIMITER //
CREATE FUNCTION `db`.`Configuration`(
p_column_name VARCHAR(128),
p_id INT
)
RETURNS VARCHAR(256)
READS SQL DATA
BEGIN
DECLARE v_config VARCHAR(256) DEFAULT NULL;
DECLARE v_parent INT;
SELECT p_column_name,`ParentID`
INTO v_config, v_parent
FROM `Table`
WHERE `ID`=p_id;
RETURN CONCAT(v_config, '::', v_parent);
END ;
//
DELIMITER ;
I have a procedure in MySQL and I want to convert it into Oracle procedure, All is ok but MySQL inbuilt function "last_insert_id()" raise the error. Is there any solution to resolve it? Can i create a function for same in oracle.
SELECT LAST_INSERT_ID();
You can't do similar function in Oracle which works extactly as in MySQL correct me someone if I am wrong.
Look at this notes in MySQL doc(http://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_last-insert-id)
For stored functions and triggers that change the value, the value is
restored when the function or trigger ends, so following statements
will not see a changed value.
Important
If you insert multiple rows using a single INSERT statement,
LAST_INSERT_ID() returns the value generated for the first inserted
row only. The reason for this is to make it possible to reproduce
easily the same INSERT statement against some other server.
In Oracle autoincrement column works since 12c and they're based on sequence. If someone calls sequence.NextVal then all sessions will see only changed values of sequence. In MySQL behavior is diffrent(look again on Important note)
Oracle and MySQL works different especially when parallel sessions works.
#krokodilko and #NicholasKrasnov tell you how can be used returning clause.
my example, if you want get ID of just now inserted row:
declare
l_id number;
begin
-- ONE ROW INSERT
l_id := someseq.nextval;
begin
insert into temp(id) values(l_id);
-- if insert fail then variable must be null
exception when others then l_id := null ;
end;
if l_id is not null then
-- do what you want RETURN this, or use for other statements
null;
-- here you have just now inserted ID in variable l_id
end if;
-- MULTIPLE INSERT
for some_data in (select * from some_joined_tables) loop
l_id := someseq.nextval;
begin
insert into temp(id) values(l_id);
exception when others then l_id := null ;
end;
if l_id is not null then
-- do what you want RETURN this, or use for other statements
-- here you have just now inserted ID in variable l_id
end if;
end loop;
-- HERE (after loop) YOU CAN RETURN LAST INSERTED VALUE of current transaction in variable l_id
end;
it's just demo of code and i'am not compiling it on server
Is it possible to write compound statements in if condition -mysql ?
It is returning an error for me like:
[Err] 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 'DECLARE n INT unsigned DEFAULT 0;
DECLARE i INT unsigned DEFAULT 0;
DECL' at line 40
Sombody please help
Here is the query I am using
...........................................
IF(tarif='LT')
THEN
CREATE TABLE costSlabs SELECT `Start`,`End`,Cost FROM energy_slabs where SiteId=NEW.SiteID and `Start` < totalUnits;
DECLARE n INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE newStart INT;
DECLARE newEnd INT;
DECLARE newCost DOUBLE;
DECLARE finalCost DOUBLE DEFAULT 0;
SELECT COUNT(*) FROM costSlabs INTO n;
SET i=0;
WHILE i<n DO
SELECT `Start`, `End`,Cost INTO newStart,newEnd,newCost FROM costSlabs LIMIT i,1 ;
IF(newEnd<totalUnits)
THEN
SET finalCost = finalCost + ((newEnd-newStart) * newCost);
ELSE
...........................................
The IF statement is valid within the context of a MySQL stored program (stored procedure, function or trigger). Outside of the context of a MySQL stored program, it isn't a valid SQL statement.
We're not seeing the context of these statements, but based on the reference to NEW. in the SQL SELECT, without any table or table alias named NEW, I'll assume that this SQL is in the context of a CREATE TRIGGER statement.
I suspect that the problem you are encountering is due to the semicolon delimiter, which effectively ends the CREATE TRIGGER statement. In order to create a trigger that includes semicolon characters, it's necessary to specify a different statement delimiter. For example:
DELIMITER $$
CREATE TRIGGER mytrig
BEFORE INSERT ON mytable
FOR EACH ROW
BEGIN
SET NEW.col1 = 'foo';
SET NEW.col2 = 'bar';
END$$
DELIMITER ;
The first statement specifies that $$ will be used as the delimiter to end a statement. With that set, the statements following will be read together as a single statement, until a $$ delimiter is encountered. That will end the statement, and allow it be executed. This basically prevents the semicolons within the trigger body from prematurely "ending" the statement.
Once the statements are completed, you can change the delimiter back to a semicolon.
The block of SQL text posted in the question is invalid on its own. I suspect that the syntax error is being returned from the execution of a CREATE TRIGGER statement, but that's just a guess, since there's not enough context in the question to make a conclusion.
Also, I believe that within a MySQL stored program (such as a TRIGGER), all of the DECLARE statements must appear before other statements, such as CREATE TABLE.
This stored procedure that I'm working on errors out some times. I am getting a Result consisted of more than one row error, but only for certain JOB_ID_INPUT values. I understand what causes this error, and so I have tried to be really careful to make sure that my return values are scalar when they should be. Its tough to see into the stored procedure, so I'm not sure where the error could be generated. Since the error is thrown conditionally, it has me thinking memory could be an issue, or cursor reuse. I don't work with cursors that often so I'm not sure. Thank you to anyone who helps.
DROP PROCEDURE IF EXISTS export_job_candidates;
DELIMITER $$
CREATE PROCEDURE export_job_candidates (IN JOB_ID_INPUT INT(11))
BEGIN
DECLARE candidate_count INT(11) DEFAULT 0;
DECLARE candidate_id INT(11) DEFAULT 0;
# these are the ib variables
DECLARE _overall_score DECIMAL(5, 2) DEFAULT 0.0;
# declare the cursor that will be needed for this SP
DECLARE curs CURSOR FOR SELECT user_id FROM job_application WHERE job_id = JOB_ID_INPUT;
# this table stores all of the data that will be returned from the various tables that will be joined together to build the final export
CREATE TEMPORARY TABLE IF NOT EXISTS candidate_stats_temp_table (
overall_score_ib DECIMAL(5, 2) DEFAULT 0.0
) engine = memory;
SELECT COUNT(job_application.id) INTO candidate_count FROM job_application WHERE job_id = JOB_ID_INPUT;
OPEN curs;
# loop controlling the insert of data into the temp table that is retuned by this function
insert_loop: LOOP
# end the loop if there is no more computation that needs to be done
IF candidate_count = 0 THEN
LEAVE insert_loop;
END IF;
FETCH curs INTO candidate_id;
# get the ib data that may exist for this user
SELECT
tests.overall_score
INTO
_overall_score
FROM
tests
WHERE
user_id = candidate_id;
#build the insert for the table that is being constructed via this loop
INSERT INTO candidate_stats_temp_table (
overall_score
) VALUES (
_overall_score
);
SET candidate_count = candidate_count - 1;
END LOOP;
CLOSE curs;
SELECT * FROM candidate_stats_temp_table WHERE 1;
END $$
DELIMITER ;
The WHERE 1 (as pointed out by #cdonner) definitely doesn't look right, but I'm pretty sure this error is happening because one of your SELECT ... INTO commands is returning more than one row.
This one should be OK because it's an aggregate without a GROUP BY, which always returns one row:
SELECT COUNT(job_application.id) INTO candidate_count
FROM job_application WHERE job_id = JOB_ID_INPUT;
So it's probably this one:
# get the ib data that may exist for this user
SELECT
tests.overall_score
INTO
_overall_score
FROM
tests
WHERE
user_id = candidate_id;
Try to figure out if it's possible for this query to return more than one row, and if so, how do you work around it. One way might be to MAX the overall score:
SELECT MAX(tests.overall_sore) INTO _overall_score
FROM tests
WHERE user_id = candidate_id
I think you want to use
LIMIT 1
in your select, not
WHERE 1
Aside from using this safety net, you should understand your data to figure out why you are getting multiple results. Without seeing the data, it is difficult for me to take a guess.