I want to create a stored procedure in Mysql that removes a certain temporary table (if it exists) and then creates this temporary table in the database 'prs1'.
The procedure that I have created is this:
CREATE PROCEDURE `CrearTablaTemporal`(table_name VARCHAR(100))
BEGIN
SET #TablaTemporal = table_name;
SET #sql_query1 = CONCAT('DROP temporary table if exists ',#TablaTemporal);
PREPARE stmt1 FROM #sql_query1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
SET #sql_query2 = CONCAT('CREATE TEMPORARY TABLE IF NOT EXISTS prs1.',#TablaTemporal,' (ean13 VarChar(13)) Engine=MyISAM');
PREPARE stmt2 FROM #sql_query2;
EXECUTE stmt2;
DEALLOCATE PREPARE stmt2;
END
When I execute the stored procedure, no error appears but if I execute the following SQL sentence it tells me that the table does not exist:
When I execute the stored procedure from my project (created in Xojo) I don't get any error but I don't know how to check if it is created perfectly since from programs with Navicat, Valentina Studio, etc. when I check the database, the table doesn't exist.
The question is, how do I keep the table open, insert values and retrieve the query?. I explain myself, my application has the connection open permanently to the database all the time until I close the application. In my method, I start the transaction, execute the sentence that creates the temporary table (thanks to the stored procedure) and finish the transaction. I don't close the connection to the database but then I don't know how to maintain the table.
How could I solve it, please?.
Thank you very much.
Sergio
Temporary tables are only available in the session in which they were created. As soon as the connection is terminated, temporary tables are dropped.
A common usecase for using temporary tables are test frameworks, where you use temporary tables to avoid further cleanup.
Session 1:
mysql> delimiter !!
mysql> CREATE PROCEDURE CreateTable(table_name VARCHAR(100))
-> BEGIN
-> SET #a:= CONCAT("CREATE OR REPLACE TEMPORARY TABLE ", table_name, " (a int)");
-> EXECUTE IMMEDIATE #a;
-> END!!
Query OK, 0 rows affected (0,01 sec)
mysql> delimiter ;
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 4118 |
+-----------------+
1 row in set (0,00 sec)
mysql> describe foobar;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| a | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
1 row in set (0,00 sec)
Session 2:
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 4119 |
+-----------------+
1 row in set (0,01 sec)
mysql> describe foobar;
ERROR 1146 (42S02): Table 'test.foobar' doesn't exist
So in case you need to check integrity or content of a temporary table, you need to do that either within the same connection or you have use non temporary tables.
Finally I have the solution with the help of all those who have written to me. I program in Xojo so the queries to the database are made from this program. With Georg's help I have realized that the temporary table only exists in that session and if you close it, the temporary table is deleted. This is how I solved my problem.
I have created a method with these steps:
// Start the transaction
DB.SQLExecute("START TRANSACTION")
// Drop the temporary table if it exists and if not we create it.
We must use these two SQL sentences:
'DROP temporary table if exists #TemporalTable'
'CREATE temporary table if NOT exists #TablaTemporal(field1 VarChar(13))
// OPTIONAL: check if the created time table exists (see below)
// Insert the values into the temporary table
Dim stmSQL As String
DB.SQLExecute("START TRANSACTION")
stmSQL = "INSERT INTO #TablaTemporal(field1)"
stmSQL = stmSQL + " VALUES(?)"
Dim ps As MySQLPreparedStatement = DB.Prepare(stmSQL)
If BBDD.Error = True Then MsgBox DB.ErrorMessage
ps.BindType( 0, MySQLPreparedStatement.MYSQL_TYPE_STRING)
ps.Bind( 0, field1)
ps.SQLExecute
DB.Commit
Return Not DB.Error
//Recover the results of the consultation against the time table
Dim stmSQL As String = "SELECT * FROM #Template"
Return DB.SQLSelect( stmSQL )
//Drop the temporary table
DROP temporary table if exists 'Temporary Table
// Finish the transaction
DB.CommitTransaction
To check and verify if the temporary table exists, I have created a "stored procedure" in the MySQL database that has this code:
CREATE PROCEDURE check_table_exists(table_name VARCHAR(100))
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET #err = 1;
SET #err = 0;
SET #table_name = table_name;
SET #sql_query = CONCAT('SELECT 1 FROM ',#table_name);
PREPARE stmt1 FROM #sql_query;
IF (#err = 1) THEN
SET #table_exists = 0;
ELSE
SET #table_exists = 1;
DEALLOCATE PREPARE stmt1;
END IF;
END
Then from Xojo we only have to call the procedure with this code:
Dim stmSQL As String = "CALL check_table_exists('#TableTemporal')""
ExecuteSQL(stmSQL) database
And execute this SQL query:
Dim stmSQL As String = "SELECT #table_exists"
Return DB.SQLSelect(stmSQL)
If the result is '1', the table has been created and exists; and if it is '0', the table does not exist.
I hope this can help someone... in my case it was for pure self-learning.
A greeting,
Sergio
Related
I have added a stored procedure to create multiple tables in dynamic SQL using WHILE loop in MySQL Workbench. The query creates last table only, instead of all. I have checked with drop table to delete the if table exists. But it still show already exists. Here is the query.
CREATE DEFINER=`root`#`localhost` PROCEDURE `weeklyLooper`(IN weeklycount INT)
BEGIN
SET #count = 0;
SET #weeklylooper = weeklycount;
SET #dumpclear = CONCAT('week' , #weeklylooper);
WHILE #count <= #weeklylooper DO
set #count = #count+1;
SET #weeklyname = CONCAT('week' , #count);
SET #weekly = CONCAT('total_' , #weeklyname, '_deposits');
SET #dropquery = CONCAT('DROP TABLE IF EXISTS `', #weeklyname, '`');
PREPARE droptablequery FROM #dropquery;
EXECUTE droptablequery;
DEALLOCATE PREPARE droptablequery;
SET #selectquery = CONCAT('CREATE temporary TABLE ', #weeklyname ,' AS SELECT sum(deposits) As ', #weekly,' FROM base0');
PREPARE selecttablequery FROM #selectquery;
EXECUTE selecttablequery;
DEALLOCATE PREPARE selecttablequery;
END WHILE;
END
Please help me to complete this.
You are creating temporary tables - temporary tables only exist for the extent of the session - since every exec is in a unique session AND DIFFERS from the session you are running the procedure in the temporary tables are never available to the session in which you are running the procedure...
or as the manual says 'You can use the TEMPORARY keyword when creating a table. A TEMPORARY table is visible only within the current session, and is dropped automatically when the session is closed.' https://dev.mysql.com/doc/refman/8.0/en/create-temporary-table.html
Consider creating permanent tables and deleting them when you are done.
i'm trying to create a trigger within my schedule table that will automatically dump the new info into the hourmap table. the issue that i'm having is that within the hourmap table, the employee names are the column headers. this means that i have to dynamically build the column header so that the input hours are placed in the appropriate column. below is the script that im currently trying to get to work but i keep running into issues. either mysql workbench will just stop working and shut down, or when i hit apply, everything appears to run successfully, however, the trigger is never actually created.
schedule table looks like this:
------------------------------
id|project_id|employee_id|hours|
--|----------|-----------|-----|
1 | 11 | 22 |8 |
--------------------------------
hourmap table looks like this:
----------------------------------------
id|ProjectID |ScheduleID |sam|jon|smith|
--|----------|-----------|---|---|-----|
----------------------------------------
the employee table looks like this;
------------
id|Employee|
--|--------|
1 |sam |
2 |jon |
22|smith |
------------
my trigger statement looks like this:
CREATE DEFINER=`root`#`localhost` TRIGGER `projectscheduler`.`schedule_AFTER_INSERT` AFTER INSERT ON `schedule` FOR EACH ROW
SET #EMPNAME=(SELECT Employee FROM employees WHERE id = NEW.employee_id);
BEGIN
INSERT INTO hourmap (ProjectID, ScheduleID, #EMPNAME)
VALUES (NEW.project_id, NEW.id, NEW.hours);
END
here is my attempt at a stored procedure, however, still no luck.
USE `projectscheduler`$$
CREATE DEFINER=`root`#`localhost` TRIGGER `projectscheduler`.`schedule_AFTER_INSERT` AFTER INSERT ON `schedule` FOR EACH ROW
BEGIN
SET #sql = NULL;
SELECT CONCAT('SELECT Emoloyee FROM employees WHERE id = NEW.id') INTO #sql;
SET #sql = CONCAT('INSERT INTO hourmap (ProjectID, ScheduleID, `',#sql,'`) VALUES(NEW.project_id, NEW.id, NEW.hours)');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
ERROR 1336: Dynamic SQL is not allowed in stored function or trigger
This is the sql i'm using based from this answer:
SET #pattern = '%_movielist';
SELECT concat('TRUNCATE TABLE ', GROUP_CONCAT(concat(TABLE_NAME)), ';')
INTO #truncatelike FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE #pattern;
SELECT #truncatelike;
PREPARE stmt FROM #truncatelike;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
but I get this error Access denied for user 'root'#'%' to database 'information_schema'.
What am I doing wrong? It seems to work for other users
You trying to execute this statement on "information_schema" database. Read more about this database [https://dev.mysql.com/doc/refman/5.7/en/information-schema.html]
You should not be running statements on the information_schema database (unless you REALLY know what you're doing). The database serves as a "meta" repository that dictates how the server operates. Chances are that you have no need to touch it and you'll likely brick your server if you do.
This is already answered here. [#1044 - Access denied for user 'root'#'localhost' to database 'information_schema'
Restriction to above: This query will work only if the no of table returned by the statement is 1 for more than 1 tables, you will require to use it in iteration.
To make this work for all the table matching the pattern we would require to use stored procedure.
Please change the Procedure name
CREATE PROCEDURE `new_procedure`()
BEGIN
-- Pattern to Match
SET #pattern = '%_movielist';
-- Temporary Table to Store the Result of The Select Statement
CREATE TEMPORARY TABLE IF NOT EXISTS Table_ToBeTruncated
(
Id int NOT NULL AUTO_INCREMENT,TableName varchar(100),
PRIMARY KEY (id)
);
-- Insert all the TableName to be Truncated
insert Table_ToBeTruncated(TableName)
SELECT distinct concat('TRUNCATE TABLE `', TABLE_NAME, '`;')
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE #pattern and TABLE_SCHEMA = 'movielist';
-- Declare a variable to count the no of records to be truncated.
SET #count=(Select count(*)from Table_ToBeTruncated);
-- Iterate the list
WHILE #count> 0 DO
-- Pick One table from the Temporary Table List;
SELECT TableName into #truncatelike from Table_ToBeTruncated where ID= #count;
-- Prepare the statement
PREPARE stmt FROM #truncatelike;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- Decrease the counter.
set #count = #count- 1;
END WHILE;
drop TEMPORARY TABLE IF EXISTS Table_ToBeTruncated ;
END
I'm trying to write MySQL script dropping some tables selected by pattern but my procedure doesn't compile. Could anybody please advice what is wrong with it please?
delimiter #
drop procedure if exists drop_audit_tables #
create procedure drop_audit_tables()
begin
declare done int default false;
declare cmd varchar(4000);
declare cmds cursor for select 'drop table [' + table_name + ']' from information_schema.tables where table_name like '%_audit';
declare continue handler for not found set done = true;
open cmds;
tLoop: loop
fetch cmds into cmd;
if done then
leave tLoop;
end if;
PREPARE STMT FROM cmd;
EXECUTE STMT;
DEALLOCATE PREPARE STMT;
end loop tLoop;
close cmds;
end #
the error message:
[42000][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 'cmd; EXECUTE STMT; DEALLOCATE PREPARE STMT; end loop tLoop; close cm' at line 13
You can avoid the cursor:
mysql> DROP TABLE IF EXISTS `one_audit`;
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS `two_audit`;
Query OK, 0 rows affected (0.01 sec)
mysql> DROP TABLE IF EXISTS `three_audit`;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE `one_audit`(`a` INT);
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE `two_audit`(`a` INT);
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE `three_audit`(`a` INT);
Query OK, 0 rows affected (0.00 sec)
mysql> SET #`drop_tables` := (
-> SELECT
-> CONCAT('DROP TABLE IF EXISTS ',
-> GROUP_CONCAT(CONCAT('`', `TABLE_NAME`, '`') SEPARATOR ', '))
-> FROM
-> `information_schema`.`TABLES`
-> WHERE
-> `TABLE_SCHEMA` = DATABASE() AND
-> `TABLE_TYPE` = 'BASE TABLE' AND
-> `TABLE_NAME` LIKE '%_audit'
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT #`drop_tables`;
+--------------------------------------------------------------+
| #`drop_tables` |
+--------------------------------------------------------------+
| DROP TABLE IF EXISTS `one_audit`, `three_audit`, `two_audit` |
+--------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> PREPARE `exec` FROM #`drop_tables`;
Query OK, 0 rows affected (0.00 sec)
Statement prepared
mysql> EXECUTE `exec`;
Query OK, 0 rows affected (0.00 sec)
mysql> DEALLOCATE PREPARE `exec`;
Query OK, 0 rows affected (0.00 sec)
You must be careful with the system variable group_concat_max_len.
UPDATE
Using cursor:
DELIMITER #
DROP PROCEDURE IF EXISTS `drop_audit_tables`#
CREATE PROCEDURE `drop_audit_tables`()
BEGIN
DECLARE `done` BOOL DEFAULT 0;
DECLARE `cmd` VARCHAR(4000);
DECLARE `cmds` CURSOR FOR
SELECT
CONCAT('DROP TABLE IF EXISTS `', `TABLE_NAME`, '`')
FROM
`information_schema`.`TABLES`
WHERE
`TABLE_SCHEMA` = DATABASE() AND
`TABLE_TYPE` = 'BASE TABLE' AND
`TABLE_NAME` LIKE '%_audit';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET `done` := 1;
OPEN `cmds`;
`tLoop`: LOOP
FETCH `cmds` INTO `cmd`;
IF `done` THEN
CLOSE `cmds`;
LEAVE `tLoop`;
END IF;
SET #`cmd` := `cmd`;
PREPARE `STMT` FROM #`cmd`;
EXECUTE `STMT`;
DEALLOCATE PREPARE `STMT`;
END LOOP `tLoop`;
SET #`cmd` := NULL;
END#
CALL `drop_audit_tables`#
DELIMITER ;
14.5.1 PREPARE
Syntax
PREPARE stmt_name FROM preparable_stmt
...
preparable_stmt is either a string literal or a user variable that
contains the text of the SQL statement.
...
Your line:
declare cmds cursor for select 'drop table [' + table_name + ']' from information_schema.tables where table_name like '%_audit';
.. uses table_name without defining it first.
Try defining it first with something like:
create procedure drop_audit_tables(IN table_name VARCHAR(64))
You may want to consider the security implications of taking a variable directly from the stored procedure and placing it into your ad-hoc query.
Still, define table_name somewhere. In this case table_name would be supplied as an argument to your stored procedure. Your task then is to gather an array of table names and run this code in a for/foreach loop.
Basic (non-robust) PHP (PDO)
/* Get the audit tables. */
$stmt = $pdo->query(`CALL get_audit_tables()`)
$tables = $stmt->fetch();
$stmt->close()
$stmt = $pdo->prepare('CALL drop_audit_tables(:table)')
/* Drop each audit table. */
foreach($tables as $table)
{
$stmt->bindParam(:table, $table, PDO::PARAM_STR)
$stmt->execute();
}
$stmt->close();
Something like that, anyway.
MySQL: CREATE PROCEDURE
Specifying a parameter as IN, OUT, or INOUT is valid only for a PROCEDURE. For a FUNCTION, parameters are always regarded as IN parameters.
PHP Manual: PDO::prepare
Prepares an SQL statement to be executed by the PDOStatement::execute() method. The SQL statement can contain zero or more named (:name) or question mark (?) parameter markers for which real values will be substituted when the statement is executed.
A solution like this would make your life easier. You only need to define a basic query that finds your audit tables. Less code. Simpler.
I have the table "mytable" that contains the "columnname" field wich is the name of a column in mytable2.
I use this one for the selection:
SET #DptScn = (SELECT columnname FROM mytable WHERE tablename = 'CustomTableName' AND fieldlabel = 'CustomField');
SET #identifiedid=144;
but, when I try:
SELECT #DptScn FROM mytable2 WHERE identifiedid = #identifiedid;
this give me NOT the content of the field but the name containted into variable #DptScn...
Any advice?
I can't use Prepared Statement because I'm in a Trigger...
UPDATE:
As suggested by spencer7593 I'm creating a procedure:
DROP PROCEDURE IF EXISTS p_t;
DELIMITER $$
CREATE PROCEDURE p_t (IN DptTcn VARCHAR(255), IN tid INT, OUT tT INT)
BEGIN
SET #DptTcn = DptTcn;
SET #tid = tid;
SET #sql = CONCAT('SELECT #DptTcn FROM mytable3 WHERE tid = #tid');
PREPARE stmt FROM #sql;
EXECUTE stmt;
END$$
DELIMITER ;
Then I try it:
SET #DptTcn = (SELECT columnname mytable WHERE tablename = 'CustomTableName' AND fieldlabel = 'CustomField');
SET #identifiedid=145;
CALL proc_ticket(#DptTcn, #identifiedid, #DptT);
But I receive a:
#2014 - Commands out of sync; you can't run this command now
One option to consider is creating a PROCEDURE that makes use of prepared statements, and then calling the the stored procedure from the trigger.
The SQL statement you execute to get the value from a particular column MUST have the column_name specified in the SQL text; this can't be derived "dynamically" in the execution of the statement.
To achieve something like this, you'll need to run two separate statements; one to get the column_name; the second to "SELECT column_name FROM". And the MySQL provided mechanism for executing that second query is a prepared statement.
Followup
Here's an example. I tried to get this built in SQLFiddle, but wasn't able to get it working (it just hung. So, here's the output from a mysql command line client instead.
(All of the statements below use the same delimiter // because we can't use a semicolon as a delimiter for the stored procedure. In SQLFiddle, we have to use the same delimiter on all statements, and the // just happens to be one of the options in SQLFiddle.)
mysql> DELIMITER //
mysql> CREATE PROCEDURE foo(IN colname VARCHAR(255), IN id INT, OUT val VARCHAR(255))
-> BEGIN
-> -- handler for "Unknown column" and "No data" exceptions
-> DECLARE EXIT HANDLER FOR 1054, 1329 BEGIN SET val = NULL; END;
-> SET #sql = CONCAT('SELECT ',colname,' INTO #val FROM t WHERE id = ',id,' LIMIT 1');
-> PREPARE stmt FROM #sql;
-> EXECUTE stmt;
-> SET val = #val;
-> END//
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE t (id INT, attr VARCHAR(4), ball VARCHAR(4))//
Query OK, 0 rows affected (0.11 sec)
mysql> INSERT INTO t VALUES (1, 'abcd','efgh'),(2,'ijkl','mnop')//
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> CALL foo('attr',1,#attr_1)//
Query OK, 0 rows affected (0.00 sec)
mysql> CALL foo('attr',2,#attr_2)//
Query OK, 0 rows affected (0.00 sec)
mysql> CALL foo('ball',1,#ball_1)//
Query OK, 0 rows affected (0.00 sec)
mysql> CALL foo('ball',2,#ball_2)//
Query OK, 0 rows affected (0.00 sec)
mysql> CALL foo('attr',777,#err_bad_id)//
Query OK, 0 rows affected (0.00 sec)
mysql> CALL foo('badcol',1,#err_badcol)//
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT #attr_1
-> , #attr_2
-> , #ball_1
-> , #ball_2
-> , #err_bad_id
-> , #err_badcol//
+---------+---------+---------+---------+-------------+-------------+
| #attr_1 | #attr_2 | #ball_1 | #ball_2 | #err_bad_id | #err_badcol |
+---------+---------+---------+---------+-------------+-------------+
| abcd | ijkl | efgh | mnop | NULL | NULL |
+---------+---------+---------+---------+-------------+-------------+
1 row in set (0.00 sec)
mysql> DELIMITER ;
you should create a SP and give the column name.
create proc dbo.TestGetData(#DptScn nvarchar(256))
as
begin
set nocount on
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = 'SELECT #DptScn FROM mytable2 WHERE identifiedid = 144'
exec sp_executesql #SQL, N'#DptScn nvarchar(256)', #DptScn =#DptScn
end
Then
exec dbo.TestGetData 'Column1'