MySQL Copy table procedure fails [duplicate] - mysql

This question already has answers here:
MySQL Stored Procedure Variable as Table Name concatenated
(2 answers)
Closed last month.
What am i doing wrong with this procedure??
# Copy tabel
CREATE PROCEDURE `table_backup`(tablename varchar(50))
begin
set #copy_from = tablename;
set #copy_to = CONCAT(tablename, `_`, DATE_FORMAT(NOW(), '%d_%m_%yt%H_%i_%s'));
CREATE TABLE #copy_to LIKE #copy_from;
INSERT #copy_to SELECT * FROM #copy_from;
end
CALL table_backup('table_name');

You will need to perform your query dynamically. This is how you can achieve it in a stored procedure:
mysql> delimiter //
mysql> create procedure dynamic_query()
-> begin
-> set #query=concat("select *from DemoTable2033 where Id=3");
-> prepare st from #query;
-> execute st;
-> end
-> //
Query OK, 0 rows affected (0.13 sec)
mysql> delimiter ;
You will need to perform the create table and insert into as dynamic query. Depending on your settings you might need to perform them as separate dynamic queries, using the same pattern.
Read more at https://www.tutorialspoint.com/implement-dynamic-sql-query-inside-a-mysql-stored-procedure

Related

MySQL use cursor to clone existing tables

I'm trying to write a MySQL stored proceedure that loops through all existing tables in my database and creates a copy/clone of each table. I'm using a cursor to loop through the table names then create a new table like this:
DELIMITER //
CREATE PROCEDURE CopyTables()
BEGIN
DECLARE finished INT DEFAULT 0;
DECLARE tableName VARCHAR(100);
DECLARE copyTableName VARCHAR(100);
DECLARE curTables
CURSOR FOR
SELECT table_name FROM INFORMATION_SCHEMA.TABLES;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN curTables;
create_loop: LOOP
FETCH curTables INTO tableName;
IF finished THEN LEAVE create_loop; END IF;
SELECT concat('Processing table ', tableName);
SET copyTableName = CONCAT('copy_',tableName);
SELECT concat('Creating table ', copyTableName);
CREATE TABLE copyTableName LIKE tableName;
END LOOP;
CLOSE curTables;
END //
DELIMITER;
But I get an error when calling the stored procedure:
> call CopyTables()
[2020-12-08 18:16:03] 1 row retrieved starting from 1 in 77 ms (execution: 15 ms, fetching: 62 ms)
[2020-12-08 18:16:03] [S1000] Attempt to close streaming result set com.mysql.cj.protocol.a.result.ResultsetRowsStreaming#7a714591 that was not registered. Only one streaming result set may be open and in use per-connection. Ensure that you have called .close() on any active result sets before attempting more queries.
Is the result set exception effectively complaining because I'm creating new tables which is effectively messing with the cursor/select? I've got additional table changes on both the original and copied table to perform, like adding new columns, creating triggers, modifying constraints.
The list of table names is not static, and this should be able to run on whatever database I need it.
Can you suggest another way to achieve this without the cursor perhaps?
The problem is that the procedure is returning multiple result sets, but your Java client is not handling that correctly.
Refer to How do you get multiple resultset from a single CallableStatement?
Another problem with your procedure is that you aren't creating tables the way you think you are.
This statement:
CREATE TABLE copyTableName LIKE tableName;
will only create a table named literally copyTableName that is like another table that is literally tableName. It will NOT use the values of variables by those names.
To do what you want, you need to use a prepared statement:
SET #sql = CONCAT('CREATE TABLE `', copyTableName, '` LIKE `', tableName, '`');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
This way the value of your variables is concatenated into an SQL statement.
Note that PREPARE only accepts a user-defined session variable, the type with the # sigil. It doesn't work with local variables you create in your procedure with DECLARE. Read https://dev.mysql.com/doc/refman/8.0/en/prepare.html and https://dev.mysql.com/doc/refman/8.0/en/user-variables.html

ADD column if not exists in mysql [duplicate]

This question already has answers here:
add column to mysql table if it does not exist
(16 answers)
Closed 4 years ago.
I keep getting an error when I run this on mysql5.7
What am I doing wrong. I basically just want to add a column if the column doesnt already exist
DROP PROCEDURE IF EXISTS ALIASCOLUMN;
DELIMITER //
CREATE PROCEDURE ALIASCOLUMN()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
ALTER TABLE 'human_api_procedure' ADD COLUMN 'alias' varchar(255);
END //
DELIMITER;
CALL ALIASCOLUMN();
DROP PROCEDURE ALIASCOLUMN;
Use the following in a stored procedure:
IF NOT EXISTS( SELECT *
FROM 'tablename'
WHERE table_name = 'tablename'
AND table_schema = 'db_name'
AND column_name = 'columnname') THEN
ALTER TABLE `human_api_procedure` ADD `alias` varchar(255) ;
END IF;
Check this link. It may help you. :)
DDL statements within a procedure have to be executed as dynamic SQL. (I think that's true, I'd need to check the docs to verify.)
Also, identifiers (table names, column names) should not be enclosed in single quotes. Single quotes are delimiters for string literals.
I'd expect this statement to fail with a syntax error:
ALTER TABLE 'human_api_procedure' ADD COLUMN 'alias' varchar(255)
This
ALTER TABLE human_api_procedure ADD COLUMN alias varchar(255)
^ ^ ^ ^
or this
ALTER TABLE `human_api_procedure` ADD COLUMN `alias` varchar(255)
^ ^ ^ ^
would be valid syntax. We could also use double quotes around identifiers, if ANSI_QUOTES is included in sql_mode.
To execute an ALTER TABLE statement in the context of a MySQL stored program, we could do something like this:
DELIMITER $$
CREATE PROCEDURE aliascolumn()
BEGIN
SET #sql = 'ALTER TABLE human_api_procedure ADD COLUMN alias varchar(255)';
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
MySQL permits routines to contain DDL statements, such as CREATE and DROP.
https://dev.mysql.com/doc/refman/5.7/en/create-procedure.html
The pattern demonstrated above, using the PREPARE/EXECUTE/DEALLOCATE would allow execution of for dynamic SQL, not just static SQL text.

MySQL Stored Procedure Variable as Table Name concatenated

I have the following query I want to execute in my stored procedure WITHOUT PREPARING the query, since this gives me problems with OUT to pass back parameters.
DELIMITER //
CREATE PROCEDURE Test (
IN CID BIGINT(20),
IN IDs LONGTEXT
)
BEGIN
#EXECUTE UNDERNEATH QUERY
SELECT * FROM CONCAT('Part1_OfTableName', CID); #CID IS CustomerID
END //
DELIMITER ;
However, this fails and I don't know how to fix the problem.
(Note that in the example I have no spaces in my table name, however in my situation I might have a space in my table name though)
PREPARE should have no bearing on your ability to successfully set OUT parameters of your procedure
SET DELIMITER //
CREATE PROCEDURE test(IN cid INT, IN ids TEXT, OUT out_int INT)
BEGIN
SET #sql = CONCAT('SELECT * FROM `table_', cid, '`', CASE WHEN ids IS NULL THEN '' ELSE CONCAT(' WHERE id IN( ', ids, ')') END);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET out_int = 1;
END//
SET DELIMITER ;
Sample usage:
mysql> CALL test(1, '2,3', #out_int);
+------+
| id |
+------+
| 2 |
| 3 |
+------+
2 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT #out_int;
+----------+
| #out_int |
+----------+
| 1 |
+----------+
1 row in set (0.00 sec)
If you need to return results from a stored procedure using sql statement that must be prepared, you can use an intermediate temp table.
BEGIN
CREATE TEMPORARY TABLE `myresults` blah blah....;
//construct and prepare select you would've used, but start it with an insert like so...
// INSERT INTO `myresults` SELECT ....
// Execute the prepared query
SELECT * FROM `myresults`;
DROP TEMPORARY TABLE `myresults`;
END
...at least I am pretty sure this technique used to work; I've been working more in MSSQL the last couple years.
Something to note:
Temporary tables are connection/session specific, so while safe from a global perspective using a generic name like myresults can be problematic if queries executed earlier on the connection/session (or by a procedure calling this one) use the same name; in practice/paranoia, I tended to use a different guid (in each procedure using this technique) as a prefix for any temporary tables generated within it.

Procedure run time

DELIMITER $
DROP PROCEDURE IF EXISTS CREATE_BACKUP$
CREATE PROCEDURE CREATE_BACKUP()
BEGIN
DECLARE BACK INT DEFAULT 0;
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'STUDENTDB'
;
SHOW_LOOP:LOOP
IF BACK = 1
THEN
LEAVE SHOW_LOOP;
END IF;
CREATE TABLE STUDENT_BACKUP
AS SELECT * FROM STUDENT;
CREATE TABLE SCORE_BACKUP
AS SELECT * FROM SCORE;
CREATE TABLE GRADE_EVENT_BACKUP
AS SELECT * FROM grade_event;
END LOOP SHOW_LOOP;
END$
DELIMITER ;
Hi, when I run this procedure, it runs more than one time. So I get an error which says "STUDENT_BACKUP table already exists" for the second time when it runs. What should I do to run it just 1 time?
In MySQL you can use CREATE TABLE IF NOT EXIST... to avoid the error occurrence. See CREATE TABLE syntax for details.
To solve your quesrion for SQL server use an INFORMATION_SCHEMA view. A similar solution is in the existing topic.

Not allowed to return a result set from a trigger

I have problem:
I created procedure :
CREATE PROCEDURE `tran_sp` ()
BEGIN
SELECT 'procedure runned!';
END
$
It's works !
mysql> CALL tran_sp()$
+-------------------+
| procedure runned! |
+-------------------+
| procedure runned! |
+-------------------+
1 row in set (0.00 sec)
Then i created trigger :
mysql> CREATE TRIGGER my_trigger1
-> AFTER INSERT ON users
-> FOR EACH ROW
-> BEGIN
-> CALL tran_sp();
-> END$
mysql> INSERT INTO users VALUES(166,156)$
ERROR 1415 (0A000): Not allowed to return a result set from a trigger
mysql>
Help me please.
The exception is I think clear enough.
You can perform additional operations inside a trigger (call a SP, perform insert / update / delete operations, ...) but all of those aren't allowed to return any result.
This means, a SP with a simple select-statement inside isn't allowed. If instead you would use this select statement within a loop for instance in order to perform updates or similar, this would be allowed, as you wouldn't return anything.
The reason is, that an insert/update/delete statement can't return anything, it can't return the result set of your stored procedure and therefor you shouldn't try to return one inside the trigger.