mysql drop table if exists inside procedure - mysql

I'm trying to apply a nested set model example with procedures. I've found many of them with this technique and in the process I've found a problem. Every time I call the procedure I get unknown table XXX. When I create the procedure I got no problem at all. The quick example:
CREATE PROCEDURE `sp_getRoleTree` (IN root INT)
READS SQL DATA
BEGIN
DECLARE rows SMALLINT DEFAULT 0;
DROP TABLE IF EXISTS ROLE_TREE;
CREATE TABLE ROLE_TREE (
nodeID INT PRIMARY KEY
) ENGINE=HEAP;
INSERT INTO ROLE_TREE VALUES (root);
SELECT * FROM ROLE_TREE;
DROP TABLE ROLE_TREE;
END;
So my question is, am I doing something wrong here (it's example code), can I disable the warning on the if exists if the code is fine? Is there a special looping inside the procedures that's causing these kind of warnings?

As a work around: try to truncate table instead of re-creating.
Do not use DROP TABLE/CREATE TABLE. Create this table once (or when you need it) and use TRUNCATE TABLE command.

MySQL generates a warning when using DROP TABLE IF EXISTS tbl; when the table does not exist. This can be confusing and perhaps counter intuitive, but it is the expected behavior.
From http://dev.mysql.com/doc/refman/5.5/en/drop-table.html
Use IF EXISTS to prevent an error from occurring for tables that do not exist. A NOTE is generated for each nonexistent table when using IF EXISTS. See Section 13.7.5.41, SHOW WARNINGS Syntax.
IF EXISTS prevents MySQL from throwing an error, which is nice, but it causes a warning if the table does not exist. There is not an option to suppress this warning.

Related

SQL error when I try to ALTER TABLE in a stored procedure

I have a stored procedure designed to generate a new, 'derived' table. In this procedure I then want to add a column using ALTER TABLE. However, despite an almost identical stored procedure working fine, and despite being able to add this manually as a stored procedure to the database using MySQL Workbench, when I pass the code to the server using SOURCE (i.e. SOURCE workload.sql), I get an error 1146 (42502) 'Table 'workload._convenor_workload' doesn't exist.' (I'm doing this in Emacs as part of a org-babel block, but this is essentially just passing raw SQL to the server.)
As background, I'm in the process of migrating SQL code from a setting where I was running it raw to create my final database to one where I'd like this code to be called via triggers.
Setup: mysql Ver 8.0.16 for macos10.14 on x86_64 (MySQL Community Server - GPL)
I've tried rewriting this as a prepared statement, was unsuccessful, and have been scouring Stack Overflow. This is my first MySQL project and my reading of the documentation suggests that ALTER TABLE is a perfectly legal thing to do in a stored procedure. It's likely that I'm making a schoolboy error somewhere but at the moment I'm banging my head.
Elsewhere in my SQL, this code works in a stored procedure (ALTER TABLE function does not throw an error):
CREATE TABLE _assessment_allocations AS SELECT Assessment_ID,
IFNULL(SUM(_total_first_marking_hours),0) AS _total_first_marking_hours_sum,
GROUP_CONCAT(DISTINCT _total_first_marking_hours_needed) AS _total_first_marking_hours_needed,
GROUP_CONCAT(DISTINCT Prog_ID) AS prog_id
FROM
_marking_workload
GROUP BY Prog_ID, Assessment_ID;
ALTER TABLE _assessment_allocations
ADD COLUMN _assessment_variance DECIMAL(5,2);
However, the code that throws the error is this (specifically, the ALTER TABLE function; I've added the stored procedure code in case this is helpful). Note that this code does not throw an error when ingested by MySQL outside a stored procedure:
USE `workload`;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `module_administration_convenor`()
-- Begin by selecting elements of the ~modules~ table
CREATE TABLE `_convenor_workload` AS
SELECT Modules.Module_Code,
Modules.Module_Name,
Modules.Module_Convenor_ID,
Modules.Module_Convenor_Share,
Modules.Student_Tally,
Modules.Additional_Hours,
Modules.Convening_Notes,
Modules.Active_Status
FROM modules;
-- Add a 'Convenor' column
ALTER TABLE `_convenor_workload` ADD COLUMN `Name` VARCHAR(255) DEFAULT 'Convenor';
\* Other stuff *\
END$$
DELIMITER ;
My aim is to avoid throwing this error. I'd like to get this stored procedure actually stored! (Just like the previous stored procedure that does much the same and does not throw an error.) I'm aware that there are some back-tick and style differences between the working and non-working code, but I'm guessing these aren't super important.
As I said, I have a strong suspicion that I'm overlooking something obvious here...
As mentioned by Solarflare in the comments, you are missing a begin so the alter table is executing as a separate action. If you wrap it with begin and end then it treats all the code as the stored procedure.
USE `workload`;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `module_administration_convenor`()
Begin
-- Begin by selecting elements of the ~modules~ table
CREATE TABLE `_convenor_workload` AS
SELECT Modules.Module_Code,
Modules.Module_Name,
Modules.Module_Convenor_ID,
Modules.Module_Convenor_Share,
Modules.Student_Tally,
Modules.Additional_Hours,
Modules.Convening_Notes,
Modules.Active_Status
FROM modules;
-- Add a 'Convenor' column
ALTER TABLE `_convenor_workload` ADD COLUMN `Name` VARCHAR(255) DEFAULT 'Convenor';
END
$$
DELIMITER ;

MYSQL if table exit run something otherwise run another thing [duplicate]

I'm stumped, I don't know how to go about doing this.
Basically I just want to create a table, but if it exists it needs to be dropped and re-created, not truncated, but if it doesn't exist just create it.
Would anyone be able to help?
Just put DROP TABLE IF EXISTS `tablename`; before your CREATE TABLE statement.
That statement drops the table if it exists but will not throw an error if it does not.
Just use DROP TABLE IF EXISTS:
DROP TABLE IF EXISTS `foo`;
CREATE TABLE `foo` ( ... );
Try searching the MySQL documentation first if you have any other problems.
Well... Huh. For years nobody mentioned one subtle thing.
Despite DROP TABLE IF EXISTS `bla`; CREATE TABLE `bla` ( ... ); seems reasonable, it leads to a situation when old table is already gone and new one has not been yet created: some client may try to access subject table right at this moment.
The better way is to create brand new table and swap it with an old one (table contents are lost):
CREATE TABLE `bla__new` (id int); /* if not ok: terminate, report error */
RENAME TABLE `bla__new` to `bla`; /* if ok: terminate, report success */
RENAME TABLE `bla` to `bla__old`, `bla__new` to `bla`;
DROP TABLE IF EXISTS `bla__old`;
You should check the result of CREATE ... and do not continue in
case of error, because failure means that other thread didn't finish
the same script: either because it crashed in the middle or just
didn't finish yet -- it's a good idea to inspect things by yourself.
Then, you should check the result of first RENAME ... and do not
continue in case of success: whole operation is successfully
completed; even more, running next RENAME ... can (and will) be
unsafe if another thread has already started same sequence (it's
better to cover this case than not to cover, see locking note below).
Second RENAME ... atomically replaces table definition, refer to
MySQL manual
for details.
At last, DROP ... just cleans up the old table,
obviously.
Wrapping all statements with something like SELECT GET_LOCK('__upgrade', -1); ... DO RELEASE_LOCK('__upgrade'); allows to just invoke all statements sequentially without error checking, but I don't think it's a good idea: complexity increases and locking functions in MySQL aren't safe for statement-based replication.
If the table data should survive table definition upgrade... For general case it's far more complex story about comparing table definitions to find out differences and produce proper ALTER ... statement, which is not always possible automatically, e.g. when columns are renamed.
Side note 1:
You can deal with views using the same approach, in this case CREATE/DROP TABLE merely transforms to CREATE/DROP VIEW while RENAME TABLE remains unchanged. In fact you can even turn table into view and vice versa.
CREATE VIEW `foo__new` as ...; /* if not ok: terminate, report error */
RENAME TABLE `foo__new` to `foo`; /* if ok: terminate, report success */
RENAME TABLE `foo` to `foo__old`, `foo__new` to `foo`;
DROP VIEW IF EXISTS `foo__old`;
Side note 2:
MariaDB users should be happy with CREATE OR REPLACE TABLE/VIEW, which already cares about subject problem and it's fine points.
I needed to drop a table and re-create with a data from a view.
I was creating a table out of a view and this is what I did:
DROP TABLE <table_name>;
CREATE TABLE <table_name> AS SELECT * FROM <view>;
The above worked for me using MySQL MariaDb.

Creating temporary tables inside stored procedure

I need to create two temporary tables inside a stored procedure, and wherever I put the CREATE statements MySQL gives me that nonsense syntax error missing 'end'.
This useful page says nothing about where to put the CREATE statements, though I have everything else is in the desired order.
I also would like to note that only the semicolon after second CREATE statement is recognized as a syntax error, if I swap them, the error is still in the latter statement.
Here's my code, but I guess only first 20 lines matter.
The statements I was (unsuccessfully) trying to add:
-- temporary tables
CREATE TEMPORARY TABLE filtered
LIKE seances;
CREATE TEMPORARY TABLE probs (
solution_id INT,
priori DOUBLE);

If table exists drop table then create it, if it does not exist just create it

I'm stumped, I don't know how to go about doing this.
Basically I just want to create a table, but if it exists it needs to be dropped and re-created, not truncated, but if it doesn't exist just create it.
Would anyone be able to help?
Just put DROP TABLE IF EXISTS `tablename`; before your CREATE TABLE statement.
That statement drops the table if it exists but will not throw an error if it does not.
Just use DROP TABLE IF EXISTS:
DROP TABLE IF EXISTS `foo`;
CREATE TABLE `foo` ( ... );
Try searching the MySQL documentation first if you have any other problems.
Well... Huh. For years nobody mentioned one subtle thing.
Despite DROP TABLE IF EXISTS `bla`; CREATE TABLE `bla` ( ... ); seems reasonable, it leads to a situation when old table is already gone and new one has not been yet created: some client may try to access subject table right at this moment.
The better way is to create brand new table and swap it with an old one (table contents are lost):
CREATE TABLE `bla__new` (id int); /* if not ok: terminate, report error */
RENAME TABLE `bla__new` to `bla`; /* if ok: terminate, report success */
RENAME TABLE `bla` to `bla__old`, `bla__new` to `bla`;
DROP TABLE IF EXISTS `bla__old`;
You should check the result of CREATE ... and do not continue in
case of error, because failure means that other thread didn't finish
the same script: either because it crashed in the middle or just
didn't finish yet -- it's a good idea to inspect things by yourself.
Then, you should check the result of first RENAME ... and do not
continue in case of success: whole operation is successfully
completed; even more, running next RENAME ... can (and will) be
unsafe if another thread has already started same sequence (it's
better to cover this case than not to cover, see locking note below).
Second RENAME ... atomically replaces table definition, refer to
MySQL manual
for details.
At last, DROP ... just cleans up the old table,
obviously.
Wrapping all statements with something like SELECT GET_LOCK('__upgrade', -1); ... DO RELEASE_LOCK('__upgrade'); allows to just invoke all statements sequentially without error checking, but I don't think it's a good idea: complexity increases and locking functions in MySQL aren't safe for statement-based replication.
If the table data should survive table definition upgrade... For general case it's far more complex story about comparing table definitions to find out differences and produce proper ALTER ... statement, which is not always possible automatically, e.g. when columns are renamed.
Side note 1:
You can deal with views using the same approach, in this case CREATE/DROP TABLE merely transforms to CREATE/DROP VIEW while RENAME TABLE remains unchanged. In fact you can even turn table into view and vice versa.
CREATE VIEW `foo__new` as ...; /* if not ok: terminate, report error */
RENAME TABLE `foo__new` to `foo`; /* if ok: terminate, report success */
RENAME TABLE `foo` to `foo__old`, `foo__new` to `foo`;
DROP VIEW IF EXISTS `foo__old`;
Side note 2:
MariaDB users should be happy with CREATE OR REPLACE TABLE/VIEW, which already cares about subject problem and it's fine points.
I needed to drop a table and re-create with a data from a view.
I was creating a table out of a view and this is what I did:
DROP TABLE <table_name>;
CREATE TABLE <table_name> AS SELECT * FROM <view>;
The above worked for me using MySQL MariaDb.

MySQl Trigger help needed

using MySQL 5.1.36, I am trying to write trigger which drops scratch tables form "scratch" database.
CREATE DEFINER=`root`#`localhost` TRIGGER
`jobq`.`DropScratch`
BEFORE DELETE ON jobq.jobq FOR EACH ROW
BEGIN
DECLARE tblname VARCHAR(128);
set tblname=concat('scratch.',OLD.jobname);
DROP TABLE IF EXISTS tblname;
END;
I am always getting an error:
Explicit or implicit commit is not allowed in stored function or trigger.
Can I somehow overcome this restriction?
Thank you beforehand
Arman
The primary problem here is that you are not allowed to drop a table within a trigger. That's what the error message is getting at when it says "implicit commit" is not allowed. The drop table does an implicit commit.
So you will need to figure out a different way to do this other than a trigger. One way would be to set up a cron job which compares the data in information_schema.tables to the jobq table to look for tables in the scratch DB that can be dropped, and then drop them.
I should also point out that the way you are trying to dynamically create a drop table statement will not work. That is going to drop a table named literally "tblname", not "scratch.jobname". If you want to drop a table dynamically you will need to build the drop table statement in a separate scripting language, such as python, perl, shell, etc.
Good luck!