I can see that there are some questions [1,2] that already ask for this, but where the solution didn't contain a complete SQL script to do this task.
I have a situation where it would be very helpful to delete all foreign keys using SQL only.
Currently I'm trying to solve this with a stored procedure and a cursor as follows:
-- No automatic commits:
DROP PROCEDURE IF EXISTS removeConstraints;
-- Magic to happen soon:
DELIMITER |
CREATE PROCEDURE removeConstraints()
BEGIN
SET AUTOCOMMIT=0;
SET FOREIGN_KEY_CHECKS=0;
-- https://dev.mysql.com/doc/refman/5.0/en/cursors.html
-- https://stackoverflow.com/questions/1745165/looping-over-result-sets-in-mysql
-- https://mariadb.com/kb/en/mariadb/cursor-overview/
DECLARE done INT DEFAULT FALSE;
DECLARE s VARCHAR(1024) DEFAULT '';
DECLARE cur CURSOR FOR SELECT CONCAT('ALTER TABLE ',TABLE_NAME, ' DROP FOREIGN KEY ', CONSTRAINT_NAME, ';')
FROM INFORMATION_SCHEMA.Key_COLUMN_USAGE
WHERE REFERENCED_TABLE_NAME IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO s;
IF done THEN
LEAVE read_loop;
END IF;
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
SET FOREIGN_KEY_CHECKS=1;
COMMIT;
SET AUTOCOMMIT=1;
END |
DELIMITER ;
-- Do magic:
CALL removeConstraints();
-- Cleanup:
DROP PROCEDURE removeConstraints;
Sadly this produces the following error message:
ERROR 1064 (42000) at line 5: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'DECLARE done INT DEFAULT FALSE;
DECLARE s VARCHAR(1024) DEFAULT '';
DECLARE ' at line 8
With the input from Ravinder Reddy I've now updated the DECLARE parts right after the BEGIN to look like this:
CREATE PROCEDURE removeConstraints()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE s VARCHAR(1024) DEFAULT '';
DECLARE cur CURSOR FOR
SELECT DISTINCT CONCAT('ALTER TABLE ',TABLE_NAME,' DROP FOREIGN KEY ',CONSTRAINT_NAME,';')
FROM INFORMATION_SCHEMA.Key_COLUMN_USAGE
WHERE TABLE_SCHEMA=DATABASE()
AND REFERENCED_TABLE_NAME IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET AUTOCOMMIT=0;
SET FOREIGN_KEY_CHECKS=0;
but when I try to execute the procedure I still get an error:
MariaDB [v4]> CALL removeConstraints();
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'NULL' at line 1
I've also tried to use a different SELECT statement like:
SELECT CONCAT('ALTER TABLE ',TABLE_NAME,' DROP FOREIGN KEY ',CONSTRAINT_NAME,';')
FROM INFORMATION_SCHEMA.Key_COLUMN_USAGE
WHERE TABLE_SCHEMA=DATABASE()
AND CONSTRAINT_NAME != 'PRIMARY'
AND CONSTRAINT_NAME IS NOT NULL
AND TABLE_NAME IS NOT NULL;
…but it didn't help.
I've got it working now by changing the code so that the CONCAT happens later.
DROP PROCEDURE IF EXISTS removeConstraints;
-- Magic to happen soon:
DELIMITER |
CREATE PROCEDURE removeConstraints()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tName VARCHAR(64);
DECLARE cName VARCHAR(64);
DECLARE cur CURSOR FOR
SELECT DISTINCT TABLE_NAME, CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.Key_COLUMN_USAGE
WHERE TABLE_SCHEMA=DATABASE()
AND REFERENCED_TABLE_NAME IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET AUTOCOMMIT=0;
SET FOREIGN_KEY_CHECKS=0;
OPEN cur;
read_loop: LOOP
FETCH cur INTO tName, cName;
IF done THEN
LEAVE read_loop;
END IF;
SET #sql = CONCAT('ALTER TABLE ',tName,' DROP FOREIGN KEY ',cName,';');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
SET FOREIGN_KEY_CHECKS=1;
COMMIT;
SET AUTOCOMMIT=1;
END |
DELIMITER ;
-- Do magic:
CALL removeConstraints();
-- Cleanup:
DROP PROCEDURE removeConstraints;
All DECLARE statements must be on top in a BEGIN - END block.
And all other statements should follow them.
In your code, you have SET statements defined before DECLARE statements.
Move those statements to below of DECLARE statements.
Sample:
-- https://dev.mysql.com/doc/refman/5.0/en/cursors.html
-- https://stackoverflow.com/questions/1745165/looping-over-result-sets-in-mysql
-- https://mariadb.com/kb/en/mariadb/cursor-overview/
DECLARE done INT DEFAULT FALSE;
DECLARE s VARCHAR(1024) DEFAULT '';
DECLARE cur CURSOR FOR
SELECT CONCAT('ALTER TABLE ',TABLE_NAME,' DROP FOREIGN KEY ',CONSTRAINT_NAME,';')
FROM INFORMATION_SCHEMA.Key_COLUMN_USAGE
WHERE REFERENCED_TABLE_NAME IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET AUTOCOMMIT=0;
SET FOREIGN_KEY_CHECKS=0;
Document Reference:
DECLARE Syntax
DECLARE is permitted only inside a BEGIN ... END compound statement
and must be at its start, before any other statements.
Declarations must follow a certain order. Cursor declarations must
appear before handler declarations. Variable and condition
declarations must appear before cursor or handler declarations
Related
I have a table with design
CREATE TABLE IF NOT EXISTS InsuranceContract (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`enquiryCode` VARCHAR(20) DEFAULT NULL,
`contractCode` VARCHAR(20) DEFAULT NULL,
`createdAt` DATETIME DEFAULT CURRENT_TIMESTAMP (),
`updatedAt` DATETIME DEFAULT CURRENT_TIMESTAMP () ON UPDATE CURRENT_TIMESTAMP (),
UNIQUE KEY (`enquiryCode`)) ENGINE=INNODB DEFAULT CHARSET=UTF8 COLLATE = UTF8_BIN;
Then I was created a procedure like this
DROP procedure IF EXISTS `sp_insurance_contract_get`;
DELIMITER $$
CREATE PROCEDURE `sp_insurance_contract_get` (enquiryCode VARCHAR(20), contractCode VARCHAR(20))
BEGIN
SET #t1 = "SELECT * FROM InsuranceContract
WHERE InsuranceContract.enquiryCode = enquiryCode
AND InsuranceContract.contractCode = contractCode;";
PREPARE param_stmt FROM #t1;
EXECUTE param_stmt;
DEALLOCATE PREPARE param_stmt;
END$$
DELIMITER ;
And I was executed this procedure in MySQL Workbench by this command:
CALL sp_insurance_contract_get('EQ000000000014', '3001002');
I expected I will receive 1 row result but it selected all records in this table.
If I copy and create exactly this #t1 into plain SQL not using statement, it's correct.
Please help me to fix this error. I'm using MySQL 8.0.19
You can use placehoders on prepare statements, this is why we use them to prevent sql injection
One other thing never use column names as variables names, databases can not differentiate
DROP procedure IF EXISTS `sp_insurance_contract_get`;
DELIMITER $$
CREATE PROCEDURE `sp_insurance_contract_get` (enquiryCode_ VARCHAR(20), contractCode_ VARCHAR(20))
BEGIN
SET #t1 = "SELECT * FROM InsuranceContract
WHERE enquiryCode = ?
AND contractCode = ?;";
PREPARE param_stmt FROM #t1;
SET #a = enquiryCode_;
SET #b = contractCode_;
EXECUTE param_stmt USING #a, #b;
DEALLOCATE PREPARE param_stmt;
END$$
DELIMITER ;
When you say
WHERE enquiryCode = enquiryCode
you compare that named column to itself. The result is true always (unless the column value is NULL).
Change the names of your SP's parameters, so you can say something like
WHERE enquiryCode_param = enquiryCode
and things should work.
Notice that you have no need of a MySql "prepared statement" here. In the MySql / MariaDb world prepared statements are used for dynamic SQL. That's for constructing statements within the server from text strings. You don't need to do that here.
We are planning to deploy a Stored Procedure to rebuild index. As per below specifictions on Mysql forum. Someone can send any sample script do the same for all table in a particular Database. Will there is any concern or any issues we could face for below script running on weekend.
ALTER TABLE t1 ENGINE = InnoDB;
https://dev.mysql.com/doc/refman/5.7/en/rebuilding-tables.html#rebuilding-tables-alter-table
Index's are kept up to date in InnoDB, statistics can get out of date. Post 5.6 even that is rarely needed to my knowledge. Running the Alter command will repair the table, which will repair any fragmentation. You can read about the different types of algorithms that are available to ALTER TABLE here:
ALTER TABLE
If however you want to check the database over, you can use the check mysql command which does ALTER as well as a few other bits n pieces to optimise index's, update statistics etc.
mysqlcheck
Below is a the mysql script to Optimize the Mysql DB. However as said earlier its rarely required since InnoDB is itself able to do the same.
USE VIPLamar;
DROP PROCEDURE IF EXISTS Lamar_Index_Rebuild_Script;
DELIMITER //
-- call Lamar_Index_Rebuild_Script();
CREATE PROCEDURE Lamar_Index_Rebuild_Script()
mysp:BEGIN
Declare v_max_counter int;
Declare v_counter int;
Declare v_query longtext;
Declare v_table_name varchar(1000);
Declare v_datetime datetime default now();
DROP temporary TABLE if exists temp_todo;
create temporary table temp_todo
(
id int auto_increment primary key,
`table_name` varchar(1000)
);
create table if not exists VIP_Lamar_Index_Rebuild_Script
(
id int auto_increment primary key,
`table_name` varchar(1000),
start_date datetime,
finish_date datetime
);
insert into temp_todo (table_name)
SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = DATABASE();
-- loop thru table
-- check if table exists
Select max(id) Into v_max_counter from temp_todo;
Set v_counter = 1;
WHILE v_counter <= v_max_counter
Do
select `table_name` into v_table_name from temp_todo where id = v_counter;
IF (EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE table_name = v_table_name AND table_schema = DATABASE()))
THEN
set v_query = concat('OPTIMIZE TABLE ' , v_table_name , ';');
set #stmt = v_query;
PREPARE tempstatement FROM #stmt;
EXECUTE tempstatement;
DEALLOCATE PREPARE tempstatement;
END if;
set v_counter = v_counter +1 ;
end while;
insert into VIP_Lamar_Index_Rebuild_Script(`table_name`,start_date,finish_date)
select table_name,v_datetime,now() from temp_todo
;
-- select * from VIP_Lamar_Index_Rebuild_Script;
-- drop table VIP_Lamar_Index_Rebuild_Script;
DROP temporary TABLE if exists temp_todo;
END;
//
DELIMITER ;
Hello The following procedure will have to move all constraints from one table to the other however I am having some difficulties at the point where the constraint should be deleted.
The problem: how do I use variables in the following line
ALTER TABLE var_referenced_table_name DROP FOREIGN KEY var_constraint_name;
when I use as is, I receive the following error
Error Code: 1146. Table 'oaf_businesslink_dev.var_referenced_table_name' doesn't exist
MySQL does not recognise var_referenced_table_name and var_constraint_name as variables.
DELIMITER //
DROP PROCEDURE IF EXISTS AlterConstraints//
CREATE PROCEDURE AlterConstraints()
BEGIN
DECLARE schema_name VARCHAR(60) DEFAULT 'oaf_businesslink_dev';
DECLARE table_name VARCHAR(60) DEFAULT 'wp_systemuser';
DECLARE finished INTEGER DEFAULT 0;
DECLARE total INTEGER DEFAULT 0;
DECLARE var_constraint_name VARCHAR(60) DEFAULT '';
DECLARE var_table_name VARCHAR(60) DEFAULT '';
DECLARE var_column_name VARCHAR(60) DEFAULT '';
DECLARE var_referenced_table_name VARCHAR(60) DEFAULT '';
DECLARE var_referenced_column_name VARCHAR(60) DEFAULT '';
DECLARE cur_constraints CURSOR FOR SELECT constraint_Name, table_name,column_name,referenced_table_name,referenced_column_name
FROM information_schema.key_column_usage
WHERE constraint_schema = schema_name
AND referenced_table_name = table_name
AND table_name IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN cur_constraints;
get_constraint:
LOOP FETCH cur_constraints
INTO var_constraint_name
,var_table_name
,var_column_name
,var_referenced_table_name
,var_referenced_column_name;
IF finished THEN
LEAVE get_constraint;
END IF;
/* Get Constraint Count */
SET total = total + 1;
/* Remove Constraint */
IF EXISTS(SELECT * FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_NAME = var_constraint_name AND TABLE_NAME = var_referenced_table_name AND TABLE_SCHEMA = schema_name)
THEN
/*
* Error Code: 1146. Table 'oaf_businesslink_dev.var_referenced_table_name' doesn't exist
*/
ALTER TABLE var_referenced_table_name DROP FOREIGN KEY var_constraint_name;
END IF;
/* Change Datatype to BIGINT */
/* Recreate Constraint to new table */
END
LOOP get_constraint;
CLOSE cur_constraints;
SELECT total;
END
//
DELIMITER ;
CALL AlterConstraints();
Thanks in advance.
With the use of variables as column names and tables, it would be best to DECLARE a query as a "string" and then execute that string via a Prepared Statement.
This can be done in two ways, either by CONCAT() to build the full string or by using PREPARE with arguments:
SET #query = CONCAT('ALTER TABLE ', var_referenced_table_name, ' DROP FOREIGN KEY ', var_constraint_name, ';');
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Hello The following procedure will have to move all constraints from one table to the other however I am having some difficulties at the point where the constraint should be deleted.
The problem: how do I use variables in the following line
ALTER TABLE var_referenced_table_name DROP FOREIGN KEY var_constraint_name;
when I use as is, I receive the following error
Error Code: 1146. Table 'oaf_businesslink_dev.var_referenced_table_name' doesn't exist
MySQL does not recognise var_referenced_table_name and var_constraint_name as variables.
DELIMITER //
DROP PROCEDURE IF EXISTS AlterConstraints//
CREATE PROCEDURE AlterConstraints()
BEGIN
DECLARE schema_name VARCHAR(60) DEFAULT 'oaf_businesslink_dev';
DECLARE table_name VARCHAR(60) DEFAULT 'wp_systemuser';
DECLARE finished INTEGER DEFAULT 0;
DECLARE total INTEGER DEFAULT 0;
DECLARE var_constraint_name VARCHAR(60) DEFAULT '';
DECLARE var_table_name VARCHAR(60) DEFAULT '';
DECLARE var_column_name VARCHAR(60) DEFAULT '';
DECLARE var_referenced_table_name VARCHAR(60) DEFAULT '';
DECLARE var_referenced_column_name VARCHAR(60) DEFAULT '';
DECLARE cur_constraints CURSOR FOR SELECT constraint_Name, table_name,column_name,referenced_table_name,referenced_column_name
FROM information_schema.key_column_usage
WHERE constraint_schema = schema_name
AND referenced_table_name = table_name
AND table_name IS NOT NULL;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN cur_constraints;
get_constraint:
LOOP FETCH cur_constraints
INTO var_constraint_name
,var_table_name
,var_column_name
,var_referenced_table_name
,var_referenced_column_name;
IF finished THEN
LEAVE get_constraint;
END IF;
/* Get Constraint Count */
SET total = total + 1;
/* Remove Constraint */
IF EXISTS(SELECT * FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_NAME = var_constraint_name AND TABLE_NAME = var_referenced_table_name AND TABLE_SCHEMA = schema_name)
THEN
/*
* Error Code: 1146. Table 'oaf_businesslink_dev.var_referenced_table_name' doesn't exist
*/
ALTER TABLE var_referenced_table_name DROP FOREIGN KEY var_constraint_name;
END IF;
/* Change Datatype to BIGINT */
/* Recreate Constraint to new table */
END
LOOP get_constraint;
CLOSE cur_constraints;
SELECT total;
END
//
DELIMITER ;
CALL AlterConstraints();
Thanks in advance.
With the use of variables as column names and tables, it would be best to DECLARE a query as a "string" and then execute that string via a Prepared Statement.
This can be done in two ways, either by CONCAT() to build the full string or by using PREPARE with arguments:
SET #query = CONCAT('ALTER TABLE ', var_referenced_table_name, ' DROP FOREIGN KEY ', var_constraint_name, ';');
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
I want to set the delimiter inside an sql file (because I cannot rely on users to do that through the terminal).
Is there a mysql statement that will allow me to set the delimiter?
Using
DELIMITER //
throws an error.
# Categories schema
# --- !Ups
CREATE TABLE IF NOT EXISTS `category` (
`id` INT NOT NULL AUTO_INCREMENT ,
`pid` INT NULL DEFAULT 0 ,
`label` VARCHAR(64) NULL ,
`active` TINYINT NULL DEFAULT 0,
PRIMARY KEY (`id`) );
DELIMITER //
CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_id(value INT) RETURNS INT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE _id INT;
DECLARE _parent INT;
DECLARE _next INT;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET #id = NULL;
SET _parent = #id;
SET _id = -1;
IF #id IS NULL THEN
RETURN NULL;
END IF;
LOOP
SELECT MIN(id)
INTO #id
FROM category
WHERE pid = _parent
AND id > _id;
IF #id IS NOT NULL OR _parent = #start_with THEN
SET #level = #level + 1;
RETURN #id;
END IF;
SET #level := #level - 1;
SELECT id, pid
INTO _id, _parent
FROM category
WHERE id = _parent;
END LOOP;
END//
DELIMITER ;
# --- !Downs
#DROP TABLE category;
We got the following error: 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 'DELIMITER / CREATE FUNCTION
hierarchy_connect_by_parent_eq_prior_id(value INT) ' at line 1
[ERROR:1064, SQLSTATE:42000], while trying to run this SQL script:
In the version of MySql I use the same error occurs when using the delimiter command, but this version handles the delimiter ";" for statements and delimiter "|" for stored procedures and functions, which i think solves the problem; try this:
-- any comma-terminated statements here (select, create table, drop table, update, delete, etc)
CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_id(value INT) RETURNS INT
-- function body here
END
|
-- other statements or functions here
Change delimiter in sql file as below:
-- # delimeter=/
-- # delimeter=;