I need to create a dynamic procedure so that whenever I need to rename a column of a table I pass the desired parameters and it executes without errors. So the verification of the IF.
The error generated by this code is as follows:
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 'IF EXISTS (SELECT * FROM information_schema.columns WHERE
table_name = 'People'' at line 1
DROP PROCEDURE IF EXISTS `change_column_name`;
DELIMITER ;;
CREATE PROCEDURE `change_column_name`(IN tableName VARCHAR(100), IN columnName VARCHAR(100), IN newColumnName VARCHAR(100), IN columnType VARCHAR(20), IN defaultValue VARCHAR(100))
BEGIN
SET #query = CONCAT('IF EXISTS (SELECT * FROM information_schema.columns WHERE table_name = \'', tableName, '\' AND column_name = \'', columnName, '\') THEN
ALTER TABLE \'', tableName, '\' CHANGE COLUMN \'', columnName, '\' \'', newColumnName, '\' ', columnType, ' DEFAULT ', defaultValue, ';
END IF;');
PREPARE stmt1 FROM #query;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END ;;
DELIMITER ;
CALL `change_column_name`('People', 'Nme', 'Name', 'VARCHAR(50)', 'NULL');
Solved, the way the steps were made were wrong.
DROP PROCEDURE IF EXISTS `change_column_name`;
DELIMITER ;;
CREATE PROCEDURE `change_column_name`(IN tableName VARCHAR(100), IN columnName VARCHAR(100), IN newColumnName VARCHAR(100), IN columnType VARCHAR(20), IN defaultValue VARCHAR(100))
BEGIN
IF EXISTS (SELECT * FROM information_schema.COLUMNS WHERE table_name = tableName AND column_name = columnName) THEN
SET #query = CONCAT('ALTER TABLE `', tableName, '` CHANGE COLUMN `', columnName, '` `', newColumnName, '` ', columnType, ' DEFAULT ', defaultValue, ';');
PREPARE stmt1 FROM #query;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END IF;
END ;;
DELIMITER ;
CALL `change_column_name`('People', 'Nme', 'Name', 'VARCHAR(50)', 'NULL');
Related
I was wondering if there's a way to check if an index exists before creating it or destroying it on MySQL. It appears that there was a feature request for this a few years back, but I can't find any documentation for a solution. This needs to be done in a PHP app using MDB2.
Here is my 4 liner:
set #exist := (select count(*) from information_schema.statistics where table_name = 'table' and index_name = 'index' and table_schema = database());
set #sqlstmt := if( #exist > 0, 'select ''INFO: Index already exists.''', 'create index i_index on tablename ( columnname )');
PREPARE stmt FROM #sqlstmt;
EXECUTE stmt;
IF EXISTS modifier is not built for DROP INDEX or CREATE INDEX yet. But you can check manually for the existence before creating/dropping an index.
Use this sentence to check whether the index already exists.
SHOW INDEX FROM table_name WHERE KEY_NAME = 'index_name'
If the query returns zero (0) then the index does not exists, then you can create it.
If the query returns a positive number, then the index exists, then you can drop it.
Here is a DROP INDEX IF EXISTS procedure:
DELIMITER $$
DROP PROCEDURE IF EXISTS drop_index_if_exists $$
CREATE PROCEDURE drop_index_if_exists(in theTable varchar(128), in theIndexName varchar(128) )
BEGIN
IF((SELECT COUNT(*) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name =
theTable AND index_name = theIndexName) > 0) THEN
SET #s = CONCAT('DROP INDEX ' , theIndexName , ' ON ' , theTable);
PREPARE stmt FROM #s;
EXECUTE stmt;
END IF;
END $$
DELIMITER ;
This code was created based on the procedure from here: Determining if MySQL table index exists before creating
I tweaked answers found here and else where to come up with the following sprocs for dropping & creating indexes. Note that the AddTableIndex sproc can drop the index if need be. They also accept a schema name which was critical for my uses.
DELIMITER //
DROP PROCEDURE IF EXISTS migrate.DropTableIndex //
CREATE PROCEDURE migrate.DropTableIndex
(
in schemaName varchar(128) -- If null use name of current schema;
, in tableName varchar(128) -- If null an exception will be thrown.
, in indexName varchar(128) -- If null an exception will be thrown.
)
BEGIN
SET schemaName = coalesce(schemaName, schema());
IF((SELECT COUNT(*) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = schemaName and table_name = tableName AND index_name = indexName) > 0) THEN
SET #s = CONCAT('DROP INDEX `' , indexName , '` ON `' , schemaName, '`.`', tableName, '`');
PREPARE stmt FROM #s;
EXECUTE stmt;
END IF;
END //
DROP PROCEDURE IF EXISTS migrate.AddTableIndex//
CREATE PROCEDURE migrate.AddTableIndex
(
IN schemaName varchar(128) -- If null use name of current schema;
, IN tableName varchar(128) -- If null an exception will be thrown.
, IN indexName varchar(128) -- If null an exception will be thrown.
, IN indexDefinition varchar(1024) -- E.g. '(expireTS_ ASC)'
, IN ifPresent ENUM('leaveUnchanged', 'dropAndReplace') -- null=leaveUnchanged.
, OUT outcome tinyint(1) -- 0=unchanged, 1=replaced, 4=added.
)
BEGIN
DECLARE doDrop tinyint(1) DEFAULT NULL;
DECLARE doAdd tinyint(1) DEFAULT NULL;
DECLARE tmpSql varchar(4096) DEFAULT '';
SET schemaName = coalesce(schemaName, schema());
SET ifPresent = coalesce(ifPresent, 'leaveUnchanged');
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema = schemaName AND table_name = tableName AND index_name = indexName) THEN
IF (ifPresent = 'leaveUnchanged') THEN
SET doDrop = 0;
SET doAdd = 0;
SET outcome = 0;
ELSEIF (ifPresent = 'dropAndReplace')
THEN
SET doDrop = 1;
SET doAdd = 1;
SET outcome = 1;
END IF;
ELSE
SET doDrop = 0;
SET doAdd = 1;
SET outcome = 4;
END IF;
IF (doDrop = 1) THEN
SET tmpSql = concat( 'alter table `', schemaName, '`.`', tableName, '` drop index `', indexName, '` ');
SET #sql = tmpSql;
PREPARE tmp_stmt FROM #sql;
EXECUTE tmp_stmt;
DEALLOCATE PREPARE tmp_stmt;
END IF;
IF (doAdd = 1) THEN
SET tmpSql = concat( 'alter table `', schemaName, '`.`', tableName, '` add index `', indexName, '` (', indexDefinition, ')');
SET #sql = tmpSql;
PREPARE tmp_stmt FROM #sql;
EXECUTE tmp_stmt;
DEALLOCATE PREPARE tmp_stmt;
END IF;
END;
//
DELIMITER ;
I have something similar with using SELECT IF() statement in MySQL.
select if (
exists(
select distinct index_name from information_schema.statistics
where table_schema = 'schema_db_name'
and table_name = 'tab_name' and index_name like 'index_1'
)
,'select ''index index_1 exists'' _______;'
,'create index index_1 on tab_name(column_name_names)') into #a;
PREPARE stmt1 FROM #a;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
The advantage of using if() statement is that, it doesn’t need a stored procedures.
I think this will helpful to you drop your existing index.
DELIMITER //
CREATE PROCEDURE dropIndexing
()
BEGIN
IF EXISTS(
SELECT * FROM information_schema.statistics
WHERE TABLE_SCHEMA = DATABASE()
AND `table_name`='mytable'
AND `index_name` = 'myindex'
)
THEN
ALTER TABLE `mytable` DROP INDEX `myindex`;
END IF;
END //
DELIMITER ;
CALL dropIndexing();
DROP PROCEDURE dropIndexing;
MySQL Workbench version 6.3 (MySql fork MariaDb)
DROP INDEX IF EXISTS FK_customer__client_school__school_id ON dbname.tablename;
I was having problems with some of the solutions presented here. This is what I came up with:
DELIMITER $$
DROP PROCEDURE IF EXISTS myschema.create_index_if_not_exists $$
CREATE PROCEDURE myschema.create_index_if_not_exists(in p_tableName VARCHAR(128), in p_indexName VARCHAR(128), in p_columnName VARCHAR(128) )
BEGIN
PREPARE stmt FROM 'SELECT #indexCount := COUNT(1) from information_schema.statistics WHERE `table_name` = ? AND `index_name` = ?';
SET #table_name = p_tableName;
SET #index_name = p_indexName;
EXECUTE stmt USING #table_name, #index_name;
DEALLOCATE PREPARE stmt;
-- select #indexCount;
IF( #indexCount = 0 ) THEN
SELECT 'Creating index';
SET #createIndexStmt = CONCAT('CREATE INDEX ', p_indexName, ' ON ', p_tableName, ' ( ', p_columnName ,')');
PREPARE stmt FROM #createIndexStmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
END $$
DELIMITER ;
Use it as follows:
call myschema.create_index_if_not_exists('MyTable','end_time_index','end_time');
This was tested on MAC OS X 10.8.2 with MySQL 5.5.24 and on Windows 7 with MySQL 5.5.21
Here is a workaround for the DROP INDEX IF EXISTS, that is missing in MySQL and MariaDB versions before v10.1.4. You can also use it for every other statement you want, that should be depend on the existence of an INDEX (e.g. for SELECT "info: index exists." like in the example below).
-- DROP INDEX IF EXISTS
SELECT
COUNT(*)
INTO
#INDEX_my_index_ON_TABLE_my_table_EXISTS
FROM
`information_schema`.`statistics`
WHERE
`table_schema` = 'my_database'
AND `index_name` = 'my_index'
AND `table_name` = 'my_table'
;
SET #statement := IF(
#INDEX_my_index_ON_TABLE_my_table_EXISTS > 0,
-- 'SELECT "info: index exists."',
'DROP INDEX `my_index` ON `my_table`',
'SELECT "info: index does not exist."'
);
PREPARE statement FROM #statement;
EXECUTE statement;
I am trying to find out if there a table called OOK and if so, do something to it. This is what I have so far, which does not work with a helpful ERROR 1064 […] syntax error message:
IF show tables like 'OOK' THEN
DELETE FROM OOK WHERE Id = 'Development';
INSERT INTO OOK VALUES ( 'Development', 'Localhost' );
END IF
This is to support some legacy code and might not be the best solution to the problem. However, it will fix it for what I need.
Since I am getting lots of syntax errors on the answers, here is the exact version I have: Server version: 5.5.60-MariaDB MariaDB Server.
You can access Information Schema, to check if the table exists or not. Also, you will need to use Dynamic SQL (to handle the case when table name does not exist)
Try something like below:
IF EXISTS (SELECT 1
FROM information_schema.tables
WHERE table_schema = 'your_database_name'
AND table_name = 'OOK') THEN
SET #s1 = 'DELETE FROM your_database_name.OOK WHERE Id = ?';
SET #a = 'Development';
PREPARE stmt1 FROM #s1;
EXECUTE stmt1 USING #a;
DEALLOCATE PREPARE stmt1;
SET #s2 = 'INSERT INTO your_database_name.OOK VALUES (?, ?)';
SET #b = 'Development';
SET #c = 'Localhost';
PREPARE stmt2 FROM #s2;
EXECUTE stmt1 USING #b, #c;
DEALLOCATE PREPARE stmt2;
END IF
I use the procedure and the function below in a table duplication process in a db:
Usage
CALL do_something('mydb', 'OOK', 'Development', 'Development', 'Localhost');
Implementation
DELIMITER $$;
CREATE PROCEDURE do_something(
IN dbName VARCHAR(255),
IN tableName VARCHAR(255),
IN id VARCHAR(255),
IN value1 VARCHAR(255),
IN value2 VARCHAR(255)
)
BEGIN
IF ( fn_table_exists(dbName, tableName) )
THEN
CALL statement(CONCAT(
'DELETE FROM ', tableName, ' WHERE Id = "', id, '"'));
CALL statement(CONCAT(
'INSERT INTO ', tableName, ' VALUES ( "', value1, '", "', value2, '" )'));
ELSE
SELECT CONCAT(
'ERROR: Table "', tableName, '" does not exist in the schema "', dbName, '".'
) AS ErrorMessage;
END IF;
END$$
DELIMITER ;
DELIMITER $$;
CREATE PROCEDURE statement(IN dynamic_statement TEXT)
BEGIN
SET #dynamic_statement := dynamic_statement;
PREPARE prepared_statement FROM #dynamic_statement;
EXECUTE prepared_statement;
DEALLOCATE PREPARE prepared_statement;
END$$
DELIMITER ;
DELIMITER $$;
CREATE FUNCTION fn_table_exists(dbName VARCHAR(255), tableName VARCHAR(255))
RETURNS TINYINT(1)
BEGIN
DECLARE totalTablesCount INT DEFAULT (
SELECT COUNT(*)
FROM information_schema.TABLES
WHERE (TABLE_SCHEMA COLLATE utf8_general_ci = dbName COLLATE utf8_general_ci)
AND (TABLE_NAME COLLATE utf8_general_ci = tableName COLLATE utf8_general_ci)
);
RETURN IF(
totalTablesCount > 0,
TRUE,
FALSE
);
END$$
DELIMITER ;
The MYSQL procedure will
1. Create TABLE1_ARCHIVE similar to TABLE1 if it is not there.
2.Insert the values from the TABLE1 into TABLE1_ARCHIVE based on date condition
3. Delete those records from TABLE1.
Link to the code:
https://paiza.io/projects/Eq7I5YGo-lt7_gu8wpQNdg?language=mysql
CREATE PROCEDURE ARCHIVE_EVENTS ( IN f_table VARCHAR(255),
IN t_table VARCHAR(255),
IN t_ts TIMESTAMP)
BEGIN
DECLARE c_sql VARCHAR(255);
DECLARE i_sql VARCHAR(255);
DECLARE d_sql VARCHAR(255);
SET #c_sql = CONCAT(' CREATE TABLE IF NOT EXISTS ', #t_table , ' LIKE ', #f_table );
PREPARE stmt FROM #c_sql;
EXECUTE stmt ;
SET #i_sql = CONCAT(' INSERT INTO ', #t_table, ' SELECT * FROM ', #f_table, ' WHERE `event_date` <= ', #t_ts);
PREPARE stmt FROM #i_sql;
EXECUTE stmt ;
COMMIT;
SET #d_sql = CONCAT(' DELETE FROM ', #f_table, ' WHERE `event_date` <= ', #t_ts);
PREPARE stmt FROM #d_sql;
EXECUTE stmt ;
COMMIT;
END;
CALL ARCHIVE_EVENTS ('TABLE1', 'TABLE1_ARCHIVE', now());
After doing the changes, the procedure looks like this and is still not working :
The error that i am getting is
ERROR 1064 (42000) at line 2: 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 '' at line 3
CREATE PROCEDURE ARCHIVE_EVENTS (IN f_table VARCHAR(255),IN t_table VARCHAR(255),IN t_ts TIMESTAMP)
BEGIN
DECLARE c_sql VARCHAR(255);
DECLARE i_sql VARCHAR(255);
DECLARE d_sql VARCHAR(255);
SET c_sql = CONCAT(' CREATE TABLE IF NOT EXISTS ', t_table , ' LIKE ', f_table);
PREPARE stmt FROM c_sql;
EXECUTE stmt ;
SET i_sql = CONCAT(' INSERT INTO ', t_table, ' SELECT * FROM ', f_table, ' WHERE `event_date` <= ', t_ts);
PREPARE stmt FROM i_sql;
EXECUTE stmt ;
COMMIT;
SET d_sql = CONCAT(' DELETE FROM ', f_table, ' WHERE `event_date` <= ', t_ts);
PREPARE stmt FROM d_sql;
EXECUTE stmt ;
COMMIT;
END;
CALL ARCHIVE_EVENTS ('TEST', 'TEST_ARCHIVE', now());
It isn't difficult to debug a procedure like this just put in a few select statements. Note #variables have to be used in the prepared statements and you have not quoted the date t_ts. And possibly you have not set delimiters.
drop procedure if exists p;
delimiter $$
CREATE PROCEDURE p (IN f_table VARCHAR(255),IN t_table VARCHAR(255),IN t_ts TIMESTAMP)
BEGIN
SET #c_sql = (select CONCAT(' CREATE TABLE IF NOT EXISTS ', t_table , ' LIKE ', f_table));
select #c_sql;
/*PREPARE stmt FROM #c_sql;
EXECUTE stmt ;
deallocate prepare stmt;
*/
SET #i_sql = (select CONCAT(' INSERT INTO ', t_table, ' SELECT * FROM ', f_table, ' WHERE `event_date` <= ',char(39), t_ts,char(39)));
select #i_sql;
/*
PREPARE stmt FROM #i_sql;
EXECUTE stmt ;
deallocate prepare stmt;
COMMIT;
*/
SET #d_sql = (select CONCAT(' DELETE FROM ', f_table, ' WHERE `event_date` <= ', char(39),t_ts, char(39)));
select #d_sql;
/*
PREPARE stmt FROM #d_sql;
EXECUTE stmt ;
deallocate prepare stmt;
COMMIT;
*/
END $$
delimiter ;
drop table users_copy;
CALL p('users', 'users_copy', now());
Yield these statements
+---------------------------------------------------+
| #c_sql |
+---------------------------------------------------+
| CREATE TABLE IF NOT EXISTS users_copy LIKE users |
+---------------------------------------------------+
1 row in set (0.00 sec)
+-----------------------------------------------------------------------------------------+
| #i_sql |
+-----------------------------------------------------------------------------------------+
| INSERT INTO users_copy SELECT * FROM users WHERE `event_date` <= '2018-05-09 19:36:54' |
+-----------------------------------------------------------------------------------------+
1 row in set (0.02 sec)
+----------------------------------------------------------------+
| #d_sql |
+----------------------------------------------------------------+
| DELETE FROM users WHERE `event_date` <= '2018-05-09 19:36:54' |
+----------------------------------------------------------------+
1 row in set (0.04 sec)
You could then test each of them if you wished or uncomment the prepared statements and let rip.
DROP PROCEDURE IF EXISTS ARCHIVE_EVENTS;
CREATE PROCEDURE ARCHIVE_EVENTS (IN f_table CHAR(100),IN t_table
CHAR(100),IN t_ts TIMESTAMP)
READS SQL DATA
COMMENT 'Test'
BEGIN
SET #c_sql = CONCAT(' CREATE TABLE IF NOT EXISTS ', t_table , ' LIKE ', f_table);
PREPARE stmt FROM #c_sql;
EXECUTE stmt ;
SET #i_sql = CONCAT(' INSERT INTO ', t_table, ' SELECT * FROM ', f_table, ' WHERE `dateTime` <= ', DATE(t_ts));
PREPARE stmt1 FROM #i_sql;
EXECUTE stmt1 ;
COMMIT;
SET #d_sql = CONCAT(' DELETE FROM ', f_table, ' WHERE `dateTime` <= ', DATE(t_ts));
PREPARE stmt2 FROM #d_sql;
EXECUTE stmt2 ;
COMMIT;
END;
CALL ARCHIVE_EVENTS ('TEST', 'WS_REL_TEST', now());
CREATE PROCEDURE ARCHIVE_EVENTS (IN f_table CHAR(100),IN t_table
CHAR(100),IN t_ts TIMESTAMP)
BEGIN
SET #c_sql = CONCAT(' CREATE TABLE IF NOT EXISTS ', t_table , ' LIKE ', f_table);
SET #i_sql = CONCAT(' INSERT INTO ', t_table, ' SELECT * FROM ', f_table, ' WHERE `event_date` <= ',char(39), t_ts,char(39));
SET #d_sql = CONCAT(' DELETE FROM ', f_table, ' WHERE `event_date` <= ',char(39), t_ts,char(39));
PREPARE stmt FROM #c_sql;
PREPARE stmt1 FROM #i_sql;
PREPARE stmt2 FROM #d_sql;
EXECUTE stmt ;
EXECUTE stmt1 ;
EXECUTE stmt2 ;
COMMIT;
END;
This is the working code, thanks to the above answers. This is the final solution for the query that worked for me.
I'm trying to create a stored procedure which will take a comma separated list as a value and push that into a select statement with an IN clause. I'm aware of find_in_set which works, but does have a performance overhead. I've also trialled a prepare statement but the problem remains.
My Example:
My parameter is sp1 and contains the value: 'valuex, valuey, valuez'.
BEGIN
set #sql = concat('select * from tablename WHERE assignedTo in (', sp1, ')');
PREPARE q FROM #sql;
execute q;
END
As expected, this throws an error since SQL will treat the value as column names. My question is how do I achieve: 'valuex','valuey','valuez' FROM 'valuex, valuey, valuez'?
You can use replace function:
BEGIN
set #sql = concat("select * from tablename WHERE assignedTo in ('", replace(sp1,",","','"), "')");
PREPARE q FROM #sql;
execute q;
END
An option is:
DELIMITER $$
DROP PROCEDURE IF EXISTS `sp_test`$$
CREATE PROCEDURE `sp_test`(`sp1` VARCHAR(50))
BEGIN
SET `sp1` := REPLACE(REPLACE(`sp1`, ', ', ','), ',', '\',\'');
SET #`query` := CONCAT('SELECT `column0`, `column1` FROM `tablename` WHERE `assignedTo` IN (\'', `sp1`, '\');');
PREPARE `stmt` FROM #`query`;
EXECUTE `stmt`;
DEALLOCATE PREPARE `stmt`;
END$$
DELIMITER ;
CALL `sp_test`('valuex, valuey, valuez');
-- SELECT `column0`, `column1` FROM `tablename` WHERE `assignedTo` IN ('valuex','valuey','valuez');
Is there any way to select from a variable table name database name and value?
I'm trying to a check the column value is exist from the below way. I forced do this way because the table name and column names and values are variable...
This is used in a Stored Procedure File.
I want something like:
SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA= 'database1'
AND TABLE_NAME= 'table1'
AND COLUMN_NAME = 'clmn_id_fk'
AND 'database1.table1.clmn_id_fk'=2218
I am not able to find a result.
You can take inspiration from a stored procedure as shown below. Change all that is necessary to achieve the solution you want, it is very important to include all the relevant restrictions and validations.
/* CODE FOR DEMONSTRATION PURPOSES */
DELIMITER $$
DROP PROCEDURE IF EXISTS `sp_check`$$
CREATE PROCEDURE `sp_check`(`p_table_schema` VARCHAR(64), `p_table_name` VARCHAR(64), `p_column_name` VARCHAR(64), `p_value` INT)
BEGIN
IF EXISTS (SELECT NULL
FROM `information_schema`.`columns`
WHERE `table_schema` = `p_table_schema` AND
`table_name` = `p_table_name` AND
`column_name` = `p_column_name`) THEN
SET #qry := CONCAT('SELECT IF(COUNT(`', `p_column_name`, '`), 1, 0) AS `EXISTS` FROM `', `p_table_schema`, '`.`', `p_table_name`, '` WHERE `', `p_column_name`, '` = ', `p_value`);
PREPARE stmt FROM #qry;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
ELSE
SELECT 0 AS `EXISTS`;
END IF;
END$$
DELIMITER ;
CALL `sp_check`('database1', 'table1', 'clmn_id_fk', 2218);