Below i have created one procedure, which took 2 parameter called company_name and SN_F,
I want to pass the table name to procedure as parameter for which i want to create cursor.
DELIMITER //
CREATE PROCEDURE transfer_t(IN company_name varchar(50), SN_f int)
BEGIN
DECLARE done BOOLEAN DEFAULT 0;
DECLARE dates_f date default null;
DECLARE high_f float default 0.0;
DECLARE low_f float default 0.0;
DECLARE open_f float default 0.0;
DECLARE close_f float default 0.0;
DECLARE volume_f int default 0;
DECLARE adj_close_f float default 0.0;
DECLARE Company_detail cursor for
select Date, high, low, open, close, volume, adj_close from company_name;
Declare CONTINUE HANDLER FOR NOT FOUND SET done=1;
OPEN Company_detail;
REPEAT
FETCH company_detail into dates_f, high_f, low_f, open_f, close_f, volume_f, adj_close_f;
insert into historic_data(sn ,Date ,High ,low ,open ,close , volume , adj_close) values (SN_f,dates_f,high_f,low_f,open_f,close_f, volume_f, adj_close_f);
until done END REPEAT;
close company_detail;
END//
DELIMITER ;
above procedure gets created successfully, but whenever i called it like,
call transfer_t('tcs_temp', 1);
it gives the following error
Error Code: 1146. Table 'test_schema.company_name' doesn't exist
Please help me solve this...
You seem to be looking to copy all records from a table name given as parameter to anther, fixed table. I don't see the need for a cursor here. SQL is a set-based language, which is built to natively perform such kind of operation. You would typically use the insert into ... select ... syntax.
On the other hand, you need dynamic SQL if you want to use the table name as variable - which your current code is missing.
The following code should do what you want:
delimiter //
create procedure transfer_t(in p_company_name varchar(50), p_sn_f int)
begin
set #p_sn_f = p_sn_f;
set #sql = concat(
'insert into historic_data(sn ,Date ,High ,low ,open ,close , volume , adj_close)',
' select ?, date, high, low, open, close, volume, adj_close from ', p_company_name
);
prepare stmt from #sql;
execute stmt using #p_sn_f;
deallocate prepare stmt;
end//
delimiter ;
Related
I am experiencing some trouble when I pass date-like strings to a input parameter of a stored procedure.
The table I try to modify has following columns:
create table localdevid.product_a(
INDX int PRIMARY KEY NOT NULL AUTO_INCREMENT,
ProdID int unsigned,
Assigned tinyint,
TesterID varchar(8),
tAss datetime);
Now I try to create a stored procedure:
use localdevid;
drop procedure if exists AssignNewDevID;
DELIMITER $$
use localdevid$$
CREATE PROCEDURE AssignNewDevID(in TableName varchar(255), in TesterName varchar(8), out DevID bigint(20))
BEGIN
#declare rightnow datetime;
set #t1=CONCAT("select SensorID into #localID from localdevid.",TableName," where ISNULL(Assigned) and INDX>1 order by INDX asc limit 1 for update");
prepare statement1 from #t1;
execute statement1;
deallocate prepare statement1;
set DevID=#localID;
set #t2=CONCAT("update localdevid.",TableName," set Assigned=4 where SensorID=",DevID);
prepare statement2 from #t2;
execute statement2;
deallocate prepare statement2;
set #t3=CONCAT("update localdevid.",TableName," set TesterID=",TesterName," where SensorID=",DevID);
prepare statement3 from #t3;
execute statement3;
deallocate prepare statement3;
commit;
END $$
DELIMITER ;
There were several issues, therefore I splitted it into the three statements to see where my problems might come from. I surely will later get it back into one statement back later on.
If I call the function the failure message changes:
call AssignNewDevID("product_a",'tester3',#id);
The script runs to statement2, this is executed successfully.
Statement3 drops Error Code 1054: "Unknown column 'tester3' in Field list.
I cannot understand why the parameter is interpreted as a field name.
It gets even stranger, if I pass a string as TesterName, which can be interpreted as a date or time.
In example, the TesterName are usually MAC-IDs, so the string is i.e. "00:08:01" (I transfer only the last 3 bytes of the MAC).
If I call it like this:
call AssignNewDevID("htpa32x32d",'00:08:01',#id);
I get error code: 1064: You have an error in your SQL syntax; check the manual...
What I am doing wrong here? Why can I concat TableName and DevID but not TesterName?
I don't see any difference here to the the other parameters.
Furthermore, I was not able to pass the current datetime to tAss. I did try the following:
declare rightnow datetime;
declare mydate varchar(20);
select DATE_FORMAT(now(),"%d.%m.%y") as mydate;
...
set #t4=CONCAT("update localdevid.",TableName," set tAss=",mydate," where SensorID=",DevID);
How can I pass basically NOW() to tAss?
OK, got it. Since I pass a string in TesterName I do need of course mark it between 'xxx' in this case.
So it works by
set #t3=CONCAT("update localdevid.",TableName," set TesterID='",TesterName,"' where SensorID=",DevID);
Same applies for the timestamp:
set #mydate=DATE_FORMAT(now(),"%d.%m.%y %h:%i:%S");
set #t4=CONCAT("update localdevid.",TableName," set tAss='",#mydate,"' where SensorID=",DevID);
I'm trying to use input parameters of a stored procedure within a cursor of the procedure.
Calling the procedure as follows results in an Error
-- -role- -table- -cond-
CALL grantRoleToUsersFromWhere('Student', 'studenten', true);
Error Code: 1146. Table 'uni4.utable' doesn't exist
This tells me that the parameter 'userTable' was not written to the variable 'uTable' OR 'uTable' is not recognized as a variable at all by the cursor Statement.
I tried different approaches storing / using the parameters. e.g. use them directly or store them in a Variable with a SET statement. However, if I try to use SET uTable=userTable; before the cursor declaration, MySQL WorkBench won't accept the Procedure declaration.
I spent quite some time on this but I think I'm missing an important yet simple part :-)
DROP PROCEDURE IF EXISTS grantRoleToUsersFromWhere;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE grantRoleToUsersFromWhere(IN grantRole VARCHAR(30), IN userTable VARCHAR(30), IN addCondition VARCHAR(50))
BEGIN
DECLARE workUser VARCHAR(30) default '';
DECLARE gRole VARCHAR(30) default grantRole;
DECLARE uTable VARCHAR(30) default userTable;
DECLARE aCond VARCHAR(50) default addCondition;
DECLARE cur1 CURSOR FOR SELECT Name FROM uTable WHERE aCond;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO workUser;
GRANT gRole TO workUser;
END LOOP;
CLOSE cur1;
END $$
DELIMITER ;
Create dynamic cursors directly is not possible. You can however use VIEW's to achieve the same thing. See sample.
CREATE PROCEDURE p1 (select_statement VARCHAR(255))
BEGIN
DECLARE v1,v2 VARCHAR(255);
DECLARE c CURSOR FOR SELECT * FROM t;
SET #v = CONCAT('create temporary table t as ',select_statement);
PREPARE stmt1 FROM #v;
EXECUTE stmt1;
OPEN c;
FETCH c INTO v1,v2;
SELECT v1,v2;
END//
' DECLARE cur1 CURSOR FOR SELECT Name FROM uTable WHERE aCond;' is not possible mysql does not do variable substitution (for table name). Your read loop is infinite because you don't declare a continuation handler and test for not found in the read loop.
i'm abit lost and i'm hoping for your help. I'm currently learning to work with Mysql and i got this procedure:
delimiter //
drop procedure price_update2 //
create procedure price_update2()
BEGIN
DECLARE n INT default 0;
DECLARE i INT default 1;
DECLARE cond varchar(255) default '';
SET cond = CONCAT('SELECT customer_id FROM customers LIMIT ',i,',1');
prepare stt FROM cond;
execute stt;
END //
for some reason i always get an error near the "cond; execute stt....". Probably it's just an easy thing, but i can't find it.
Also tried without concat:
delimiter //
drop procedure price_update2 //
create procedure price_update2()
BEGIN
DECLARE n INT default 0;
DECLARE i INT default 1;
DECLARE cond varchar(255) default '';
SET cond = 'SELECT customer_id FROM customers LIMIT 1';
prepare stt FROM cond;
execute stt;
END //
if i use the statement directly, it works fine.
This is an odd thing, but you need to use user-defined variables at this point:
delimiter //
drop procedure price_update2 //
create procedure price_update2()
BEGIN
DECLARE n INT default 0;
DECLARE i INT default 1;
SET #cond = CONCAT('SELECT customer_id FROM customers LIMIT ',i,',1');
prepare stt FROM #cond;
execute stt;
DEALLOCATE PREPARE stt; # don't forget to deallocate
END //
The manual states this:
A statement prepared in stored program context cannot refer to stored procedure or function parameters or local variables because they go out of scope when the program ends and would be unavailable were the statement to be executed later outside the program. As a workaround, refer instead to user-defined variables, which also have session scope
Currently, our application's database stores all DATETIME values in GMT-8, which is generally very bad. We're trying to convert the application and all data to use UTC.
I am trying to write a one-off stored procedure that will query my database's schema, find all columns of type DATETIME or TIMESTAMP, and automatically UPDATE the columns by converting values with the CONVERT_TZ() function.
Using all of the information I've found so far about cursors, dynamic statement preparation, and error handling, I've come up with this:
DELIMITER $$
CREATE PROCEDURE `sp_convertDBtoUTC`()
BEGIN
DECLARE `_rollback` BOOL DEFAULT 0;
DECLARE done INT DEFAULT 0;
DECLARE tname VARCHAR(64);
DECLARE cname VARCHAR(64);
DECLARE dt_columns CURSOR FOR
SELECT table_name, column_name
FROM information_schema.columns
WHERE table_schema = 'my_schema'
AND data_type IN ('datetime', 'timestamp')
ORDER BY table_name, column_name;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `_rollback` = 1;
START TRANSACTION;
OPEN dt_columns;
table_loop:
LOOP
FETCH dt_columns INTO tname, cname;
IF done = 1 THEN
LEAVE table_loop;
END IF;
SET #stmt = CONCAT('UPDATE ',tname,' SET ',cname, ' = CONVERT_TZ(',cname, ', \'America/Los_Angeles\', \'UTC\')');
PREPARE stmt FROM #stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE dt_columns;
IF `_rollback` THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END$$
This seems to run without errors, but does nothing. If I add a SELECT #stmt statement in there, I get multiple result sets with the correct SQL, e.g. UPDATE tz_test SET dts = CONVERT_TZ(dts, 'America/Los_Angeles', 'UTC'). If I run this statement manually, the table gets updated as expected.
However, when running the stored procedure to update the entire database, the data in the tables doesn't actually get updated, and I don't understand why.
I'm using the following as a test:
CREATE TABLE tz_test (id INT auto_increment, dts DATETIME, PRIMARY KEY (id));
INSERT INTO tz_test (dts) VALUES (NOW());
SELECT * FROM tz_test;
CALL sp_convertDBtoUTC();
SELECT * FROM tz_test;
I would have expected the second result set to contain the modified datetime value, but the two result sets are identical. Why is this?
When trying to run these queries through MySQL Workbench, they were actually failing silently because of Safe Mode (no mass update/delete without a reference to a primary key).
Once I turned it off and reconnected, the entire process worked flawlessly.
I would like to use LIMIT in a cursor. The cursor should be used and updated several times within a loop, each time with different parameters of LIMIT. Here some code:
DELIMITER $$
CREATE PROCEDURE `updateIt`() READS SQL DATA
BEGIN
declare done int(1) default 0;
declare counter int(10) default 0;
declare xabc int(10) default 0;
declare tab1Cursor cursor for select abc from tab1 limit 100000*counter, 100000;
declare continue handler for not found set done=1;
loopCounter: LOOP
set done = 0;
open tab1Cursor;
igmLoop: loop
fetch tab1Cursor into xabc;
if done = 1 then leave igmLoop; end if;
-- do something
end loop igmLoop;
close tab1Cursor;
if (counter = 1039)
leave loopCounter;
end if;
set counter = counter + 1;
END LOOP loopCounter;
END $$
DELIMITER ;
This, however, does not work (I also tried it with the cursor in the LOOP counterLoop). Can Mysql deal with dynamic cursors?
From the MySQL Manual
a cursor cannot be used for a dynamic statement that is prepared and
executed with PREPARE and EXECUTE. The statement for a cursor is
checked at cursor creation time, so the statement cannot be dynamic.
However there are 2 ways, according to this post in mysql forums:
The first is for cases where absolutely only one user at a time will be running the procedure. A prepare statement can be used to create a view with the dynamic SQL and the cursor can select from this statically-named view. There's almost no performance impact. Unfortunately, these views are also visible to other users (there's no such thing as a temporary view), so this won't work for multiple users.
Analogously, a temporary table can be created in the prepare statement and the cursor can select from the temporary table. Only the current session can see a temporary table, so the multiple user issue is resolved. But this solution can have significant performance impact since a temp table has to be created each time the proc runs.
Bottom line: We still need cursors to be able to be created dynamically!
Here's an example of using a view to pass the table name and column name into a cursor.
DELIMITER //
DROP PROCEDURE IF EXISTS test_prepare//
CREATE PROCEDURE test_prepare(IN tablename varchar(255), columnname varchar(50))
BEGIN
DECLARE cursor_end CONDITION FOR SQLSTATE '02000';
DECLARE v_column_val VARCHAR(50);
DECLARE done INT DEFAULT 0;
DECLARE cur_table CURSOR FOR SELECT * FROM test_prepare_vw;
DECLARE CONTINUE HANDLER FOR cursor_end SET done = 1;
SET #query = CONCAT('CREATE VIEW test_prepare_vw as select ', columnname, ' from ', tablename);
select #query;
PREPARE stmt from #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
OPEN cur_table;
FETCH cur_table INTO v_column_val;
WHILE done = 0 DO
SELECT v_column_val;
FETCH cur_table INTO v_column_val;
END WHILE;
CLOSE cur_table;
DROP VIEW test_prepare_vw;
END;
//
DELIMITER ;