Delete records from table if table exists - mysql

I have a lot of delete queries but in some production databases some table is not found. I need simple decision on this problem.
Queries like
DELETE b.* FROM `table` b
JOIN `another_table` u
ON u.key2 = b.key
WHERE u.key3 <> ?
I need something like:
IF TABLE `table` EXISTS DELETE b.* FROM `table` b ...

If you need to delete the rows of a table if that latter exists, you will have to rely on a custom stored procedure I think. something like that:
-- Define the procedure
CREATE PROCEDURE delete_if_exists(IN tbl CHAR(80))
BEGIN
IF #tbl IN (select table_name from information_schema.tables where table_schema = "db_2_44afb7")
THEN
SET #query = CONCAT("DELETE FROM ", #tbl);
PREPARE stmt FROM #query;
EXECUTE stmt;
END IF;
END //
-- use it
CALL delete_if_exists("mytable");
CALL delete_if_exists("myothertable");
See http://sqlfiddle.com/#!2/2ee190/3 for an example.Of course, you could adjust the "DELETE FROM",... part to fit with your specific needs.

You can enclose the SELECT query on the table before using the DROP...
IF EXISTS(SELECT table.records FROM table WHERE ...)
DELETE records_names FROM ...
From what it sounds like IF EXISTS(select query here) before your delete command is what you're looking for? I'm a postgresql guy so please excuse the SQL errors if they exist.
More examples in this post.

Related

Search Specific Range of Tables mySQL

I have a question that is plaguing me. I have a mysql database that is being used for historical data. Basically every year the system writes a new table in the database housing the time data.
so it is like this:
tables:
year_2017
year_2018
year_2019
and in each table there is data.
What I would like to do is build a query that will search through these specific tables.
Now there are other tables in there I don't want to search through. I only want the year_* tables.
Basically I would want SELECT * FROM year_*
I know you could do SELECT * FROM year_2017, year_2018, year_2019 but the problem is that every year there is going to be a new table so I would have to edit the query every year and I don't want to do that.
Any help would be great.
From the information_schema.tables, find the names of the tables that contain the year_ and then place the table names in a temporary table. Then fetch each table with a data loop.
CREATE PROCEDURE myproc()
BEGIN
CREATE TEMPORARY TABLE temp(
tablename varchar(100)
);
INSERT INTO temp
SELECT t.TABLE_NAME
FROM information_schema.tables t
WHERE t.TABLE_NAME LIKE '%year_%';
WHILE (Select Count(*) From temp) > 0 DO
SET #tablename= (Select tablename From temp limit 1);
SET #sql= concat("select * from ", #tablename);
prepare exec from #sql;
execute exec;
Delete From temp Where tablename=#tablename;
END WHILE;
END;
CALL myproc();
demo in db<>fiddle
You can use a variable to store the table name. But you'd need to come up with an automation to fill the table name variables.
mysql prepared statements
/* MYSQL */
SET #table = 't1';
SET #s = CONCAT('SELECT * FROM ', #table);
PREPARE stmt3 FROM #s;
EXECUTE stmt3;

Get condition of WHERE clause from table

I have table1 which has only 2 columns: id and condition. For example:
id condition
--------------------
100 caption LIKE "%xyz%"
200 tag=5
300 color>153
...
The user sends an id to the server, and a specific select query must be run on table2 based on the condition of that id. For example, if id 100 is sent to server, then this query must be run:
SELECT * FROM table2 WHERE caption LIKE "%xyz%"
How to get the condition from table1 and run the query with that condition? I have already tried this:
SELECT * FROM table2 INNER JOIN table1 AS t1 ON t1.id=... WHERE t1.condition
However, I get the following warning with no result.
Warning: #1292 Truncated incorrect DOUBLE value: 'caption LIKE "%xyz%"'
This is possible in MySQL 5+ using prepared statements. You can create a procedure with a condition id as an argument:
DELIMITER //
CREATE PROCEDURE get_from_table2_by_condition_id(IN conditionId bigint)
BEGIN
SET #condition = NULL;
SELECT cond
INTO #condition
FROM table1
WHERE id = conditionId;
SET #sql = CONCAT('SELECT * FROM table2 WHERE ', COALESCE(#condition, 'FALSE'));
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END//
DELIMITER ;
CALL get_from_table2_by_condition_id(100);
See a working example in this fiddle
I don't particularly recommend storing SQL code as you are describing.
But you have a client and a server. The solution is simply to query table1 for the condition and then construct the query that you want from that. This requires two queries.
You could set up a stored procedure inside the database that uses dynamic SQL for the same purpose.
There are several reasons why this is not desirable:
The code can introduce syntax errors which are quite hard to debug.
The code is subject to SQL injection, depending on the security features around table1.
Changes to the underlying tables might invalidate the conditions.
What is an alternative? One possibility is to create separate views for the different conditions. Or, just create one query and pass in parameters:
select t2.*
from table2 t2
where (caption like :caption or :caption is null) and
(tag = :tag or :tag is null) and
(color > :color or :color is null);
That is not possible, and if it where possible that would be a huge stored SQL injection security hole. To be able to do this, you will need something that will parse the stored condition and evaluate the condition on the fly. As far as I'm aware, MySQL has nothing built in for this.

using mysql variable to hold comma separated value to be used for where in clause

I have to run a query like this (query 1) -
select something from sometable where someId in (1,2,3)
I would like to keep a variable for the IDs part, like this (query 2) -
set #myIds = "1,2,3";
select something from sometable where someId in (#myIds);
But this does not give the expected result (gives an empty result set), and no query error as well.
I checked that if I wrap the comma separated IDs inside quotes, the query results an empty result set (query 3) -
select something from sometable where someId in ("1,2,3");
I guess when I am using variable #myIds like I showed above (query 2), it is evaluating to the above query (query 3).
You need to have a dynamic sql on this,
SET #myIds = '1,2,3';
SET #sql = CONCAT('select something from sometable where someId in (',#myIds,')');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQLFiddle Demo
The proper (and also more complicated) way to do that would be a temp table:
DROP TEMPORARY TABLE IF EXISTS `some_tmp_table`
CREATE TEMPORARY TABLE `some_tmp_table` (
`id` INT(10) UNSIGNED NOT NULL
) ENGINE=MEMORY #memory engine is optional...
Insert your ID's the temp table
INSERT INTO some_tmp_table VALUES (1),(2),(3)...
and then use a JOIN instead of IN().
SELECT something
FROM sometable s
JOIN some_tmp_table ts ON ts.id = s.someId
The other way is to use dynamic SQL as the other answer suggests. It might be simpler for you to generate the dynamic SQL in your app, but you can do it in MySQL too.

Duplicating a MySQL table, indices, and data

How do I copy or clone or duplicate the data, structure,
and indices of a MySQL table to a new one?
This is what I've found so far.
This will copy the data and the structure,
but not the indices:
create table {new_table} select * from {old_table};
This will copy the structure and indices,
but not the data:
create table {new_table} like {old_table};
To copy with indexes and triggers do these 2 queries:
CREATE TABLE new_table LIKE old_table;
INSERT INTO new_table SELECT * FROM old_table;
To copy just structure and data use this one:
CREATE TABLE new_table AS SELECT * FROM old_table;
I've asked this before:
Copy a MySQL table including indexes
Apart from the solution above, you can use AS to make it in one line.
CREATE TABLE tbl_new AS SELECT * FROM tbl_old;
MySQL way:
CREATE TABLE recipes_new LIKE production.recipes;
INSERT recipes_new SELECT * FROM production.recipes;
Go to phpMyAdmin and select your original table then select "Operations" tab in the "Copy table to (database.table)" area. Select the database where you want to copy and add a name for your new table.
I found the same situation and the approach which I took was as follows:
Execute SHOW CREATE TABLE <table name to clone> : This will give you the Create Table syntax for the table which you want to clone
Run the CREATE TABLE query by changing the table name to clone the table.
This will create exact replica of the table which you want to clone along with indexes. The only thing which you then need is to rename the indexes (if required).
The better way to duplicate a table is using only DDL statement. In this way, independently from the number of records in the table, you can perform the duplication instantly.
My purpose is:
DROP TABLE IF EXISTS table_name_OLD;
CREATE TABLE table_name_NEW LIKE table_name;
RENAME TABLE table_name TO table_name_OLD;
RENAME TABLE table_name _NEW TO table_name;
This avoids the INSERT AS SELECT statement that, in case of table with a lot of records can take time to be executed.
I suggest also to create a PLSQL procedure as the following example:
DELIMITER //
CREATE PROCEDURE backup_table(tbl_name varchar(255))
BEGIN
-- DROP TABLE IF EXISTS GLS_DEVICES_OLD;
SET #query = concat('DROP TABLE IF EXISTS ',tbl_name,'_OLD');
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- CREATE TABLE GLS_DEVICES_NEW LIKE GLS_DEVICES;
SET #query = concat('CREATE TABLE ',tbl_name,'_NEW LIKE ',tbl_name);
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- RENAME TABLE GLS_DEVICES TO GLS_DEVICES_OLD;
SET #query = concat('RENAME TABLE ',tbl_name,' TO ',tbl_name,'_OLD');
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- RENAME TABLE GLS_DEVICES_NEW TO GLS_DEVICES;
SET #query = concat('RENAME TABLE ',tbl_name,'_NEW TO ',tbl_name);
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END//
DELIMITER ;
Have a nice day!
Alex
To duplicate a table and its structure without data from a different a database use this. On the new database sql type
CREATE TABLE currentdatabase.tablename LIKE olddatabase.tablename
Worths to mention the importance of checking if the table already exists before
trying to duplicate it:
CREATE TABLE IF NOT EXISTS new_table LIKE old_table;
INSERT new_table
SELECT * FROM old_table;
As was said by the previous answers this will copy structure, data and
all the dependent objects of the table.
See MySql Tutorial:
Expanding on this answer one could use a stored procedure:
CALL duplicate_table('tableName');
Which will result in a duplicate table called tableName_20181022235959 If called when
SELECT NOW();
results:
2018-10-22 23:59:59
Implementation
DELIMITER $$
CREATE PROCEDURE duplicate_table(IN tableName VARCHAR(255))
BEGIN
DECLARE schemaName VARCHAR(255) DEFAULT SCHEMA();
DECLARE today VARCHAR(14) DEFAULT REPLACE(REPLACE(REPLACE(NOW(), '-', ''), ' ', ''), ':', ''); -- update # year 10000
DECLARE backupTableName VARCHAR(255) DEFAULT CONCAT(tableName, '_', today);
IF fn_table_exists(schemaName, tableName)
THEN
CALL statement(CONCAT('CREATE TABLE IF NOT EXISTS ', backupTableName,' LIKE ', tableName));
CALL statement(CONCAT('INSERT INTO ', backupTableName,' SELECT * FROM ', tableName));
CALL statement(CONCAT('CHECKSUM TABLE ', backupTableName,', ', tableName));
ELSE
SELECT CONCAT('ERROR: Table "', tableName, '" does not exist in the schema "', schemaName, '".') AS ErrorMessage;
END IF;
END $$
DELIMITER ;
DELIMITER $$
CREATE FUNCTION fn_table_exists(schemaName 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 = schemaName COLLATE utf8_general_ci)
AND (TABLE_NAME COLLATE utf8_general_ci = tableName COLLATE utf8_general_ci)
);
RETURN IF(
totalTablesCount > 0,
TRUE,
FALSE
);
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 ;
After I tried the solution above, I come up with my own way.
My solution a little manual and needs DBMS.
First, export the data.
Second, open the export data.
Third, replace old table name with new table name.
Fourth, change all the trigger name in the data (I use MySQL and it show error when I don't change trigger name).
Fifth, import your edited SQL data to the database.
To create table structure only use this below code :
CREATE TABLE new_table LIKE current_table;
To copy data from table to another use this below code :
INSERT INTO new_table SELECT * FROM current_table;
Simple Cloning:
it create a table from another table without taking into account any column attributes and indexes.
CREATE TABLE new_table SELECT * FROM original_table;
Shallow Cloning:
This will only create an empty table base on the structure of the original table
CREATE TABLE new_table LIKE original_table;
The following command would create an empty table base on the original table.
CREATE TABLE adminUsers LIKE users;
Deep Cloning:
This means the new table gets to have all the attributes of each column and indexes of the existing table. This quite useful if you want to maintain the indexes and attributes of the existing table.
CREATE TABLE new_table LIKE original_table;
INSERT INTO new_table SELECT * FROM original_table;
https://towardsdatascience.com/how-to-clone-tables-in-sql-dd29586ec89c
// To copy specific column data use this one:
CREATE TABLE ut_axis_existrec LIKE ut_karvy_annexure; // To create new table
INSERT INTO ut_axis_existrec
(funding_ac,micr_no, warrant_no,
amount,invname,mfundcode,funding_dt,status,remarks1,amc_remark,created_at)
SELECT
t1.funding_ac,
t1.micr_no,
t1.warrant_no,
t1.amount,
t1.invname,
t1.mfund_code,
t1.funding_dt,
t1.status,
t1.remarks1,
t1.created_at
from ut_axis_karvy
inner join
ut_axis_karvy_master as t2
on t1.micr_no = t2.micr_no;
Try this :
`CREATE TABLE new-table (id INT(11) auto_increment primary key) SELECT old-table.name, old-table.group, old-table.floor, old-table.age from old-table;`
I selected 4 columns from old-table and made a new table.
FOR MySQL
CREATE TABLE newtable LIKE oldtable ;
INSERT newtable SELECT * FROM oldtable ;
FOR MSSQL
Use MyDatabase:
Select * into newCustomersTable from oldCustomersTable;
This SQL is used for copying tables, here the contents of oldCustomersTable will be copied to newCustomersTable.
Make sure the newCustomersTable does not exist in the database.

Using ALTER to drop a column if it exists in MySQL

How can ALTER be used to drop a column in a MySQL table if that column exists?
I know I can use ALTER TABLE my_table DROP COLUMN my_column, but that will throw an error if my_column does not exist. Is there alternative syntax for dropping the column conditionally?
I'm using MySQL version 4.0.18.
For MySQL, there is none: MySQL Feature Request.
Allowing this is arguably a really bad idea, anyway: IF EXISTS indicates that you're running destructive operations on a database with (to you) unknown structure. There may be situations where this is acceptable for quick-and-dirty local work, but if you're tempted to run such a statement against production data (in a migration etc.), you're playing with fire.
But if you insist, it's not difficult to simply check for existence first in the client, or to catch the error.
MariaDB also supports the following starting with 10.0.2:
DROP [COLUMN] [IF EXISTS] col_name
i. e.
ALTER TABLE my_table DROP IF EXISTS my_column;
But it's arguably a bad idea to rely on a non-standard feature supported by only one of several forks of MySQL.
There is no language level support for this in MySQL. Here is a work-around involving MySQL information_schema meta-data in 5.0+, but it won't address your issue in 4.0.18.
drop procedure if exists schema_change;
delimiter ';;'
create procedure schema_change() begin
/* delete columns if they exist */
if exists (select * from information_schema.columns where table_schema = schema() and table_name = 'table1' and column_name = 'column1') then
alter table table1 drop column `column1`;
end if;
if exists (select * from information_schema.columns where table_schema = schema() and table_name = 'table1' and column_name = 'column2') then
alter table table1 drop column `column2`;
end if;
/* add columns */
alter table table1 add column `column1` varchar(255) NULL;
alter table table1 add column `column2` varchar(255) NULL;
end;;
delimiter ';'
call schema_change();
drop procedure if exists schema_change;
I wrote some more detailed information in a blog post.
I know this is an old thread, but there is a simple way to handle this requirement without using stored procedures. This may help someone.
set #exist_Check := (
select count(*) from information_schema.columns
where TABLE_NAME='YOUR_TABLE'
and COLUMN_NAME='YOUR_COLUMN'
and TABLE_SCHEMA=database()
) ;
set #sqlstmt := if(#exist_Check>0,'alter table YOUR_TABLE drop column YOUR_COLUMN', 'select ''''') ;
prepare stmt from #sqlstmt ;
execute stmt ;
Hope this helps someone, as it did me (after a lot of trial and error).
I just built a reusable procedure that can help making DROP COLUMN idempotent:
-- column_exists:
DROP FUNCTION IF EXISTS column_exists;
DELIMITER $$
CREATE FUNCTION column_exists(
tname VARCHAR(64),
cname VARCHAR(64)
)
RETURNS BOOLEAN
READS SQL DATA
BEGIN
RETURN 0 < (SELECT COUNT(*)
FROM `INFORMATION_SCHEMA`.`COLUMNS`
WHERE `TABLE_SCHEMA` = SCHEMA()
AND `TABLE_NAME` = tname
AND `COLUMN_NAME` = cname);
END $$
DELIMITER ;
-- drop_column_if_exists:
DROP PROCEDURE IF EXISTS drop_column_if_exists;
DELIMITER $$
CREATE PROCEDURE drop_column_if_exists(
tname VARCHAR(64),
cname VARCHAR(64)
)
BEGIN
IF column_exists(tname, cname)
THEN
SET #drop_column_if_exists = CONCAT('ALTER TABLE `', tname, '` DROP COLUMN `', cname, '`');
PREPARE drop_query FROM #drop_column_if_exists;
EXECUTE drop_query;
END IF;
END $$
DELIMITER ;
Usage:
CALL drop_column_if_exists('my_table', 'my_column');
Example:
SELECT column_exists('my_table', 'my_column'); -- 1
CALL drop_column_if_exists('my_table', 'my_column'); -- success
SELECT column_exists('my_table', 'my_column'); -- 0
CALL drop_column_if_exists('my_table', 'my_column'); -- success
SELECT column_exists('my_table', 'my_column'); -- 0
Chase Seibert's answer works, but I'd add that if you have several schemata you want to alter the SELECT thus:
select * from information_schema.columns where table_schema in (select schema()) and table_name=...
You can use this script, use your column, schema and table name
IF EXISTS (SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TableName' AND COLUMN_NAME = 'ColumnName'
AND TABLE_SCHEMA = SchemaName)
BEGIN
ALTER TABLE TableName DROP COLUMN ColumnName;
END;
Perhaps the simplest way to solve this (that will work) is:
CREATE new_table AS SELECT id, col1, col2, ... (only the columns you actually want in the final table)
FROM my_table;
RENAME my_table TO old_table, new_table TO my_table;
DROP old_table;
Or keep old_table for a rollback if needed.
This will work but foreign keys will not be moved. You would have to re-add them to my_table later; also foreign keys in other tables that reference my_table will have to be fixed (pointed to the new my_table).
Good Luck...
I realise this thread is quite old now, but I was having the same problem.
This was my very basic solution using the MySQL Workbench, but it worked fine...
get a new sql editor and execute SHOW TABLES to get a list of your tables
select all of the rows, and choose copy to clipboard (unquoted) from the context menu
paste the list of names into another editor tab
write your query, ie ALTER TABLE x DROP a;
do some copying and pasting, so you end up with separate query for each table
Toggle whether the workbench should stop when an error occurs
Hit execute and look through the output log
any tables which had the table now haven't
any tables which didn't will have shown an error in the logs
then you can find/replace 'drop a' change it to 'ADD COLUMN b INT NULL' etc and run the whole thing again....
a bit clunky, but at last you get the end result and you can control/monitor the whole process and remember to save you sql scripts in case you need them again.