Do something if a table exists - mysql

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 ;

Related

How to verify if index exists in a table before dropping the index on MySql? [duplicate]

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;

How to convert mysql/mariadb row in triggers (NEW/OLD) into json

how i can convert a row inside a mysql/mariadb trigger into an json object with new JSON features?
BEGIN
CALL my_audit_insert(tableName, id, ... JSON_OBJECT(NEW) ...);
END
Is there any possibility to get programatically columns of NEW or OLD?
First Try - Create a Statement
Idea is to get colums from system tables and get each value from NEW/OLD programatically
BEGIN
SET #s = 'SELECT NEW.? INTO #result';
PREPARE stmt FROM #s;
SET #a = 'id';
EXECUTE stmt USING #a;
CALL audit_insert(NEW.id, 'pages', JSON_ARRAY(result));
END
(1336): Dynamic SQL is not allowed in stored function or trigger
Second Idea - Select the row via PrimaryKey as JSON_Object in after-triggers
procedure spGetJson from https://stackoverflow.com/a/35957518/7080961
DROP PROCEDURE IF EXISTS `spGetJson`;
DELIMITER //
CREATE DEFINER=`root`#`%` PROCEDURE `spGetJson`(pTableName varchar(45), pId int, out pJson JSON)
begin
select group_concat(concat("'", COLUMN_NAME, "', ", COLUMN_NAME) separator ',')
into #cols
from information_schema.columns
where TABLE_NAME = pTableName and TABLE_SCHEMA = database();
set #q = concat('select json_object(', #cols, ') INTO #a from ', pTableName);
if pId is not null then
set #q = concat(#q, ' where id = ', pId);
end if;
set #q = concat(#q, ';');
prepare statement from #q;
execute statement;
deallocate prepare statement;
SET pJson = #a;
end//
DELIMITER;
After Insert Trigger:
BEGIN
CALL spGetJson('pages', NEW.id, #a);
CALL audit_insert(NEW.id, 'pages', #a);
END
same: (1336): Dynamic SQL is not allowed in stored function or trigger
Conclusion:
have to wait for this feature: https://bugs.mysql.com/bug.php?id=89366
or switch to postresql

Dynamic procedure to change Column name

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');

Mysql Stored Procedure Ignores Parameters and WHERE conditions

I have the following which works great:
SET #param = '%string%';
SELECT *
FROM tbl
WHERE IF(#param IS NOT NULL, LIKE #param, TRUE);
However when I change to this:
DELIMITER //
CREATE PROCEDURE test(IN param VARCHAR(255))
BEGIN
SET #param = param;
SELECT *
FROM tbl
WHERE IF(#param IS NOT NULL, LIKE #param, TRUE);
END //
DELIMITER ;
CALL test('%string%');
All rows are returned as if there were no parameter passed in.
I assume that there is some limitation or restriction with MySQL that I am missing?
Not sure but please try this with prepared statement
DELIMITER //
CREATE PROCEDURE test(IN param VARCHAR(255))
BEGIN
SET #param = param;
SET #q = CONCAT("SELECT *
FROM tbl
WHERE IF(",#param," IS NOT NULL, LIKE ",#param,", TRUE)");
PREPARE stmt FROM #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;
This statement is syntactically wrong and won't get compiled:
WHERE IF(#param IS NOT NULL, LIKE #param, TRUE);
and you can't just place variable values like that to construct an SQL statement.
You need a Prepared Statement to prepare and execute such statements.
Try the following: (you need to replace column_name with valid column name from your table.
DELIMITER //
DROP PROCEDURE IF EXISTS test //
CREATE PROCEDURE test( IN param VARCHAR( 255 ) )
BEGIN
SET #param = param;
SET #query = CONCAT( 'SELECT * FROM tbl WHERE column_name LIKE ' );
SET #query = CONCAT( #query, ' IF( \'', #param, '\' IS NOT NULL, \'' );
SET #query = CONCAT( #query, #param, '\', TRUE )' );
PREPARE stmt FROM #query;
EXECUTE stmt;
DROP PREPARE stmt; -- deallocate prepare stmt;
END;
//
DELIMITER ;
CALL test('%string%');
If you run select #query, it will return following statement:
SELECT * FROM tbl
WHERE column_name LIKE IF( '%string%' IS NOT NULL, '%string%', TRUE )

Stored procedure error when being called

When I call my procedure, it return an error:
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 'NULL' at line 1
How to fix it, do I have an error in my procedure code?
Here is my stored procedure :
DELIMITER $$
DROP PROCEDURE IF EXISTS `gamedb`.`ALIAS#SEARCH`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `ALIAS#SEARCH`(
in section_id varchar(255),
in category_id varchar(255),
in content_id varchar(255)
)
BEGIN
declare q varchar(4000);
set #q = 'SELECT * FROM tbl_alias WHERE ALIAS_ACTIVE_STATUS=1';
IF section_id IS NOT NULL THEN
set #q = concat(q,' AND ALIAS_SECTION_ID = ',section_id);
END IF;
IF category_id IS NOT NULL THEN
set #q = concat(q,' AND ALIAS_CATEGORY_ID = ',category_id);
END IF;
IF content_id IS NOT NULL THEN
set #q = concat(q,' AND ALIAS_CONTENT_ID = ',content_id);
END IF;
set #q= concat(q,' ORDER BY ALIAS_ID DESC');
prepare stmt from #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
There several problems
Since you can PREPARE only from user variable, you don't need a local variable q
You're missing # for #q variable when using CONCAT()
set #q = concat(q,' AND ALIAS_SECTION_ID = ',section_id);
^
Since your IN parameters defined as VARCHAR you better quote their values when you build the query
That being said your SP might look like this
DELIMITER $$
CREATE PROCEDURE `ALIAS#SEARCH`(
in section_id varchar(255),
in category_id varchar(255),
in content_id varchar(255)
)
BEGIN
SET #q = 'SELECT * FROM tbl_alias WHERE ALIAS_ACTIVE_STATUS = 1';
IF section_id IS NOT NULL THEN
SET #q = CONCAT(#q, ' AND ALIAS_SECTION_ID = ''', section_id, '''');
END IF;
IF category_id IS NOT NULL THEN
SET #q = CONCAT(#q, ' AND ALIAS_CATEGORY_ID = ''', category_id, '''');
END IF;
IF content_id IS NOT NULL THEN
SET #q = CONCAT(#q, ' AND ALIAS_CONTENT_ID = ''', content_id, '''');
END IF;
SET #q = CONCAT(#q, ' ORDER BY ALIAS_ID DESC');
PREPARE stmt FROM #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;