I wrote store procedure in mysql. Step were followed this website http://www.mysqltutorial.org/mysql-cursor/
But it doesn't work. Here is code
DELIMITER $$
USE `hr`$$
DROP PROCEDURE IF EXISTS `at_getShift`$$
CREATE DEFINER=`root`#`%` PROCEDURE `at_getShift`()
BEGIN
DECLARE finished BOOLEAN DEFAULT FALSE;
DECLARE employeeID VARCHAR(255);-- Default "";
-- declare cursor for employee email
DECLARE hrEmployee CURSOR FOR SELECT EmployeeID FROM h_employees WHERE EmployeeID IN ('100013', '100014');
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = TRUE;
DROP TABLE IF EXISTS temp;
CREATE TABLE IF NOT EXISTS temp(
`Code` VARCHAR(255)
);
OPEN hrEmployee;
get_employee: LOOP
FETCH hrEmployee INTO employeeID;
INSERT INTO temp(`Code`) VALUE (employeeID);
-- If no any row, leave loop
IF finished THEN
INSERT INTO temp(`Code`) VALUE ("112");
CLOSE hrEmployee;
LEAVE get_employee;
END IF;
-- insert temp
INSERT INTO temp(`Code`) VALUE ("111");
END LOOP get_employee;
SELECT * FROM temp;
END$$
DELIMITER ;
Execute: CALL at_getShift();
Result is:
2 rows in temp table ( 1 null, 1 is 112)
Please kindly help me to resolve this trouble.
In a SQL statement in MySQL stored program, the references to procedure variables take precedence over references to columns.
That is, when an identifier in a SQL statement matches a procedure variable, the SQL statement references the procedure variable.
References that are qualified with the table name or table alias reference columns from the table, even when there is a procedure variable with the same name.
Demonstration:
CREATE TABLE emp (id INT);
INSERT INTO emp (id) VALUES (101),(102);
DELIMITER $$
CREATE PROCEDURE foo()
BEGIN
DECLARE id INT DEFAULT 3;
-- this query returns 3 for all rows in emp
-- because "id" is a reference to the procedure variable
SELECT id FROM emp WHERE id = 3;
-- this query returns no rows
-- because "id" is a reference to the procedure variable
SELECT id FROM emp WHERE id = 101;
-- this query references columns in the table because
-- references to "id" are qualified
SELECT t.id FROM emp t WHERE t.id = 101;
END$$
DELIMITER ;
CALL foo;
The first query returns value of procedure variable for all rows from emp
id
-----
3
3
second query returns no rows
id
-----
third query returns references "id" column in table:
id
-----
101
The takeaway are two "best practices":
qualify all column references in a SQL statement in a procedure
and
procedure variable names should differ from names of columns, the usual pattern is to use a distinctive prefix on variables. As a trivial example: v_id, v_name, etc.
Both of these practices make it easier for a human reader to decipher a procedure.
Distinctive naming of procedure variables does reduce the chances of collisions, but does not invalidate the "best practice" of qualifying all column references in SQL statements. Both of those serve to make the author's intent more clear to the human reader.
EDIT:
I attempted to answer the question I thought you were asking... "Why is my procedure not doing what I expect it to?".
Beyond the answer to the question you asked... the operation that your procedure appears to be performing (populating a temporary table with a set of rows) that operation could be performed much faster and more efficiently by processing the rows as a set, rather than issuing painfully inefficient individual insert statements for each row. In terms of performance, a cursor loop processing RBAR (row-by-agonizing-row) is going to eat your lunch. And your lunch box.
DELIMITER $$
CREATE PROCEDURE `at_getShift_faster`()
BEGIN
-- ignore warning message when dropping a table that does not exist
DECLARE CONTINUE HANDLER FOR 1305 BEGIN END;
DROP TABLE IF EXISTS temp;
CREATE TABLE IF NOT EXISTS temp(`Code` VARCHAR(255));
INSERT INTO temp (`Code`)
SELECT h.EmployeeID
FROM h_employees h
WHERE h.EmployeeID IN ('100013', '100014')
;
SELECT * FROM temp;
END$$
DELIMITER ;
Related
When I call this stored procedure it shows error: unknown column...
BEGIN
if (
`LastRow.Transaction`=4 and `LastRow.Xre`>1)
then
SELECT
sleep(2);
END if;
end
Please note that sleep(2) is just to demonstrate to do something if condition is true. What would be the proper way to accomplish a test based on value of a specific record? In the above example the table (actually a View) has only one row.
Q: What would be the proper way to accomplish a test based on value of a specific record?
If you mean, based on values in columns stored in one row of a table... it seems like we would need a query that references the table that retrieve the values stored in the row. And then we can have those values available in the procedure.
As an example
BEGIN
-- local procedure variables, specify appropriate datatypes
DECLARE lr_transaction BIGINT DEFAULT NULL;
DECLARE lr_xre BIGINT DEFAULT NULL;
-- retrieve values from columns into local procedure variables
SELECT `LastRow`.`Transaction`
, `LastRow`.`Xre`
INTO lr_transaction
, lr_xre
FROM `LastRow`
WHERE someconditions
ORDER BY someexpressions
LIMIT 1
;
IF ( lr_transaction = 4 AND lr_xre > 1 ) THEN
-- do something
END IF;
END$$
That's an example of how we can retrieve a row from a table, and do some check. We could also do the check with SQL and just return a boolean
BEGIN
-- local procedure variables, specify appropriate datatypes
DECLARE lb_check TINYINT(1) UNSIGNED DEFAULT 0;
-- retrieve values from columns into local procedure variables
SELECT IF(`LastRow`.`Transaction` = 4 AND `LastRow`.`Xre` > 1,1,0)
INTO lb_check
FROM `LastRow`
WHERE someconditions
ORDER BY someexpressions
LIMIT 1
;
IF ( lb_check ) THEN
-- do something
END IF;
END$$
Using a single MySQL procedure i need to write queries to get data from a table (where the results can be of a list also) after getting the results,need to insert those selected rows into another table of the same database..I'm finding dificulty in getting the result of the select query and fetching values to insert into another table...
Iam able to do the above one if it returns only one row but in my case it can return any number of rows...
DELIMITER $$
USE `scmn_nov21`$$
DROP PROCEDURE IF EXISTS `procedure1`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `procedure1`(
IN Param1 VARCHAR(255),
OUT Param2 VARCHAR(255),
OUT Param3 VARCHAR(255)
)
BEGIN
DECLARE myvar TEXT;
SET myvar = (SELECT column1 FROM table1 WHERE column1 =2);
INSERT INTO table1 (column1,column2,column3)
VALUES (myvar,'Malaysia','Asia');
COMMIT;
END$$
DELIMITER ;
I believe that you can do a Create as select: http://dev.mysql.com/doc/refman/5.7/en/create-table-select.html
Returning malassia and asia as fixed values for your query...
I'm not very familiar with MySQL stored procedures, but am attempting to write one for the first time. In my procedure, I have 2 in parameters and one or both of them could be null. I need to create a cursor to loop over, but my cursor needs to be based on the in parameters. Where if 1 is null and the other isn't, my cursor query is different.
For example:
CREATE PROCEDURE test (IN date timestamp, IN id int(11))
BEGIN
DECLARE cur CURSOR FOR
IF timestamp IS NOT NULL THEN
IF id IS NULL THEN
SELECT columns FROM Table WHERE modified_on <= timestamp
ELSE
SELECT columns FROM Table WHERE userid = id AND modified_on <= timestamp
ELSE
/* Logic here to pull query without the timestamp and where userid matches if the id passed in is not null */
END IF
END
Could someone show me a simple example of how to achieve this?
issue
syntax error, the declare cursor statement requires to be associated with exactly one select query :
DECLARE cursor_name CURSOR FOR select_statement
also Table is a reserved keyword and needs to be escaped ( or use a different name ) in :
SELECT columns FROM Table
to fix, either create two cursors one for each scenario or embed both query paths in one select query
setup
create table `Table`
(
id integer primary key auto_increment not null,
userid integer not null,
modified_on datetime not null
);
fix
-- option one : combine queries into one
drop procedure if exists test;
delimiter $$
CREATE PROCEDURE test (IN date timestamp, IN id int(11))
BEGIN
DECLARE cur CURSOR FOR SELECT columns FROM `Table` WHERE ( id is null or userid = id ) and modified_on <= timestamp;
-- open and read from cursor..
END$$
delimiter ;
-- option two define two cursors and use conditional logic below to decide which to read from
drop procedure if exists test;
delimiter $$
CREATE PROCEDURE test (IN date timestamp, IN id int(11))
BEGIN
DECLARE cur1 CURSOR FOR SELECT columns FROM `Table` WHERE modified_on <= timestamp;
DECLARE cur2 CURSOR FOR SELECT columns FROM `Table` WHERE userid = id AND modified_on <= timestamp;
-- evaluate if/else logic here to decide which cursor to open and use..
END$$
delimiter ;
note: not sure what you're planning todo for each cursor fetch. depending on your use case, its possible you can do this without a cursor. if this is the case, dont use a cursor and keep the processing closer to the natural sql set-based processing
reference
mysql declare cursor syntax
ansi sql reserved keywords
mysql cursors
I have 3 existing tables: transactions, transaction_types, and expenses:
$> USE `mydb`;
Datebase changed.
$> SHOW TABLES;
expenses
transactions
transaction_types
After adding a stored procedure and 2 triggers on expenses that call the procedure, I'm no-longer able to insert into the table:
$> INSERT INTO `expenses` SET `date`='2013-12-22';
1109. Unknown table 'expenses' in field list
$> INSERT INTO `expenses` (`date`) VALUES('2013-12-22');
1109. Unknown table 'expenses' in field list
But I can select from the able just fine…
If I drop the triggers, I can insert into expenses again.
My 2 triggers are duplicates, 1 for update, 1 for insert:
USE `mydb`;
DELIMITER $$
DROP TRIGGER IF EXISTS `expense_updated_paid`
CREATE TRIGGER `expense_updated_paid`
AFTER UPDATE ON `expenses` FOR EACH ROW
BEGIN
CALL `expense_paid`( NEW.`id` , NEW.`date paid` , NEW.`amount`);
END$$
And the procedure:
USE `mydb`;
DROP procedure IF EXISTS `expense_paid`;
DELIMITER $$
CREATE PROCEDURE `expense_paid`(IN `expense_id` INT, IN `date` DATE, IN `amount` INT)
BEGIN
IF `expenses`.`date paid` IS NOT NULL THEN
SET #type_id = (SELECT `id` FROM `transaction_types` WHERE `name` = 'reimbursement');
INSERT INTO `transactions`
SET
`transactions`.`date` = `date`,
`transactions`.`amount` = `amount`,
`transactions`.`type_id` = #type_id,
`transactions`.`note` = `expense_id`;
END IF;
END$$
I expect MySQL to complain about something in the trigger or procedure if that's causing a problem instead of telling me the table just doesn't exist…
Now that you provided the procedure code, the answer is clear:
IF `expenses`.`date paid` IS NOT NULL THEN
The procedure has no context for expenses.*. That is, you can't use qualified column names inside the procedure when the qualifier refers to a query outside the procedure. This makes more sense if you accept that column qualifiers refer to correlation names in a given query, not to the table itself.
But the procedure does have the date input parameter, which you passed as the same value NEW.\date paid``. So change the line to the following:
IF `date` IS NOT NULL THEN
The following procedure gives me an error when I invoke it using the CALL statement:
CREATE DEFINER=`user`#`localhost` PROCEDURE `emp_performance`(id VARCHAR(10))
BEGIN
DROP TEMPORARY TABLE IF EXISTS performance;
CREATE TEMPORARY TABLE performance AS
SELECT time_in, time_out, day FROM attendance WHERE employee_id = id;
END
The error says "Unknown table 'performance' ".
This is my first time actually using stored procedures and I got my sources from Google. I just cant figure out what I am doing wrong.
I've tidied it up a little for you and added example code. I always keep my parameter names the same as the fields they represent but prefix with p_ which prevents issues. I do the same with variables declared in the sproc body but prefix with v_.
You can find another one of my examples here:
Generating Depth based tree from Hierarchical Data in MySQL (no CTEs)
drop procedure if exists emp_performance;
delimiter #
create procedure emp_performance
(
in p_employee_id varchar(10)
)
begin
declare v_counter int unsigned default 0;
create temporary table tmp engine=memory select time_in, time_out
from attendance where employee_id = p_employee_id;
-- do stuff with tmp...
select count(*) into v_counter from tmp;
-- output and cleanup
select * from tmp order by time_in;
drop temporary table if exists tmp;
end#
delimiter ;
call emp_performance('E123456789');
By default MySQL config variable sql_notes is set to 1.
That means that
DROP TEMPORARY TABLE IF EXISTS performance;
increments warning_count by one and you get a warning when a stored procedure finishes.
You can set sql_notes variable to 0 in my.cnf or rewrite stored procedure like that:
CREATE DEFINER=`user`#`localhost` PROCEDURE `emp_performance`(id VARCHAR(10))
BEGIN
SET ##session.sql_notes = 0;
DROP TEMPORARY TABLE IF EXISTS performance;
CREATE TEMPORARY TABLE performance AS
SELECT time_in, time_out, day FROM attendance WHERE employee_id = id;
SET ##session.sql_notes = 1;
END