MySQL: IF table exists, truncate and insert ELSE create - mysql

Working only with MySQL (I have essentially no PHP knowledge), I need to have a table that's essentially a subset from a much larger table. The source table changes from time to time, losing some entries, gaining other new ones, and values changing for existing ones. I can describe what I want to happen, but can't seem to figure out a syntax of commands to make it work. I also know I can have two separate queries and just run whichever one I need, and I have that worked out, but I'd like to combine them if possible. Here's what I want:
IF the subset_table DOES NOT EXIST, create it as [select query], ELSE truncate the subset_table and insert [select query]
Like I said, I know there are other ways to do this - I could drop if exists/create, or I could just have two different sql files to run. I just want to know if I can do this as specified above.
Thoughts?

You can do this:
create table if not exists <tablename> . . .;
truncate table <tablename>;
insert into <tablename>(cols)
select blah blahblah . . .;
You don't need any if statements at all.

This can also be done through an SP (stored procedure)... makes it more readable and safe
DELIMITER $$
DROP PROCEDURE IF EXISTS `create_table_sp`$$
CREATE PROCEDURE `create_table_sp`()
BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.TABLES WHERE table_name = '<table_name>'
AND table_schema = DATABASE() AND table_type = 'BASE TABLE') THEN
CREATE TABLE <subset_table_name>
AS SELECT * FROM <main_table_name>;
ELSE
TRUNCATE TABLE <subset_table_name>;
INSERT INTO <subset_table_name>
AS SELECT * FROM <main_table_name>;
END IF;
END$$
DELIMITER ;
CALL `create_table_sp`;
DROP PROCEDURE IF EXISTS `create_table_sp`;
There is also another way,
You could pass the table names as arguments to the SP, in this case sub_table_name and main_table_name
Make the above DML statements to a string using CONCAT()
Create a prepared statement out of it and execute
Hope this helped....

Related

Stored Procedure to read text file and loop through it

I tried looking through a lot of threads on this but couldn't find anything that works for me.
I have a text file with list of table names (about 100 out of 300 total tables in the schema).
tables.txt
table1
table2
table3
I am writing a stored procedure and want to loop through the table names in that text file and inject the table names in the query.
Something like this
While (line = readline) != null do
update line set col1='foo' where id=x;
End while;
Is something like that even possible in a stored procedure? using (mysql 5.6). and I am using Workbench 6.3 CE
Since you want to run the same code for every table in your database.You can create your command using Information Schema
USE INFORMATION_SCHEMA;
SELECT
CONCAT("ALTER TABLE `", TABLE_SCHEMA,"`.`", TABLE_NAME, "` ALTERING CODE HERE;")
AS MySQLCMD FROM TABLES
WHERE TABLE_SCHEMA = "your_schema_goes_here";
When the commands are created then you can copy and run all of them.
UPDATE If you want to update a column common between all those tables you can write it as follow
SELECT CONCAT("UPDATE `", TABLE_SCHEMA,"`.`", TABLE_NAME, "` SET COLUMN_NAME = VALUE [ANY WHERE CLAUSE]") as MySQLCMD from TABLES where TABLE_SCHEMA = "YourSchemaName";
UPDATE2 Generating commands for specific tables.
SELECT
CONCAT("ALTER TABLE `", TABLE_SCHEMA,"`.`", TABLE_NAME, "` ALTERING CODE HERE;")
AS MySQLCMD FROM TABLES
WHERE TABLE_SCHEMA = "your_schema_goes_here" AND TABLE_NAME IN ('Table1', 'TAble2');
In general I would take this approach. There is not enough detail to be more specific. Note, this uses dynamic SQL rather than a stored procedure.
Backup your database!!
Create a temporary table. (either real or in memory depending on how long you need this data retained)
Parse the text file into a temporary table using something like this:
LOAD DATA INFILE '/tmp/mylist.txt' INTO TABLE MyTempList;
Create a test a single ALTER table statement. Once you know it works exactly as intended...
Convert your working ALTER statement into dynamic SQL that selects the table names from your temporary table. Something like this: Alter table using sub select
When complete, you can delete your temporary table.
Alternately, you could put a single ALTER statement into a stored procedure, asking for the table name as a parameter. But then you will still need to parse the text file using something like steps 1-3. And then use a cursor to loop over the table names and call the stored procedure.

Execute INSERT outside of a procedure using CASE WHEN

I need to INSERT into table_2 values coming from a SELECT result set, if and only if a specific column exists in table_1, then that column has to be dropped from table_1. Else nothing should happen.
Is it all about DELIMITER?
As far as I know, no, since code isn't part of a procedure declaration. I tried it anyway but it did not seem to help. It is getting tricky for such a tiny task to run.
My code so far, which triggers syntax errors warnings from Workbench:
CASE
-- MysqlWorkbench complains (CASE is underlined) saying …
-- «Syntax error: 'CASE (case) is not valid input at this position'»
WHEN EXISTS (
SELECT * FROM information_schema.`COLUMNS`
WHERE `TABLE_SCHEMA` = 'bd_name'
AND `TABLE_NAME` = 'table_1'
AND `COLUMN_NAME` = 'name'
) THEN
INSERT INTO table_2 (caption,c_id)
SELECT DISTINCT label, c_id FROM bd_name.table_1;
ALTER TABLE table_1 DROP COLUMN label;
-- I'm not sure about how should I separate statements
-- regarding https://dev.mysql.com/doc/refman/5.7/en/case.html
END;
-- ("END" is underlined) Syntax error: extraneous input found - expected end of input
Thank you.
I don't think MySQL allows procedural constructs outside of routines, triggers, events, etc.... One way around this is to wrap your code in a proc declaration, and then follow it with a call to the proc and a drop of the proc.
Something like this:
DELIMITER $$
CREATE PROCEDURE foo()
BEGIN
Your stuff here;
and here;
END$$
DELIMITER ;
CALL foo();
DROP PROCEDURE foo();

Having various troubles with Insert Into

I have an extension I have made and inside this extension there is an "install.sql" file which contains various inserts.
Some of these inserts do it to tables which MAY exist or not depending on the clients site. What I would like to know is how to do for example something like:
"create table if exists" but for an "Insert Into".
This is an example of what im doing:
INSERT INTO `#__virtuemart_shipmentmethods_en_gb`
(virtuemart_shipmentmethod_id, shipment_name, shipment_desc, slug)
VALUES ((SELECT MAX(vs.virtuemart_shipmentmethod_id)
FROM `#__virtuemart_shipmentmethods` vs), 'Kiala','', 'kiala');
Thanks in advance for your help.
i don't know if i got your question right....
you want to insert somesthing and if its already there you want to update ?
if yes, you propably looking for THIS HERE otherwise your question is set up bad or its to late for me to get it right :)
INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
if you don't know if the table exists than you might should check all befor
usual i use pdo, don't know what you use but my solution would be some like this
$result = mysql_query("SHOW TABLES LIKE '#__virtuemart_shipmentmethods_en_gb'");
$tExists = mysql_num_rows($result) > 0;
if(!$tExists)
// Create Table Code here
Have you considered running this is a stored procedure? Something like
CREATE PROCEDURE `testprocdure` ()
BEGIN
IF (SELECT COUNT(*)
FROM information_schema.tables
WHERE table_schema = '[database name]'
AND table_name = '#__virtuemart_shipmentmethods_en_gb') = 1 THEN
INSERT INTO `#__virtuemart_shipmentmethods_en_gb` (virtuemart_shipmentmethod_id, shipment_name, shipment_desc, slug)
VALUES ((SELECT MAX(vs.virtuemart_shipmentmethod_id) FROM `#__virtuemart_shipmentmethods` vs), 'Kiala','', 'kiala');
ELSE
-- Handle if the table doesnt exists
SELECT ''; -- This is just so the SP wont return a error after the else. Remove this when you want to run it
END IF;
END
You could also make use of parameters if you have variable changing.
Then the create syntax should look like this
CREATE PROCEDURE `testprocdure` (IN p_TableName VARCHAR(100))
Change the name of the parameter and datatype to fit your needs.
Could this maybe help?

MySql - Create Table If Not Exists Else Truncate?

Here is the updated question:
the current query is doing something like:
$sql1 = "TRUNCATE TABLE fubar";
$sql2 = "CREATE TEMPORARY TABLE IF NOT EXISTS fubar SELECT id, name FROM barfu";
The first time the method containing this is run, it generates an error message on the truncate since the table doesn't exist yet.
Is my only option to do the CREATE TABLE, run the TRUNCATE TABLE, and then fill the table? (3 separate queries)
original question was:
I've been having a hard time trying to figure out if the following is possible in MySql without having to write block sql:
CREATE TABLE fubar IF NOT EXISTS ELSE TRUNCATE TABLE fubar
If I run truncate separately before the create table, and the table doesn't exist, then I get an error message. I'm trying to eliminate that error message without having to add any more queries.
This code will be executed using PHP.
shmuel613, it would be better to update your original question rather than replying. It's best if there's a single place containing the complete question rather than having it spread out in a discussion.
Ben's answer is reasonable, except he seems to have a 'not' where he doesn't want one. Dropping the table only if it doesn't exist isn't quite right.
You will indeed need multiple statements. Either conditionally create then populate:
CREATE TEMPORARY TABLE IF NOT EXISTS fubar ( id int, name varchar(80) )
TRUNCATE TABLE fubar
INSERT INTO fubar SELECT * FROM barfu
or just drop and recreate
DROP TABLE IF EXISTS fubar
CREATE TEMPORARY TABLE fubar SELECT id, name FROM barfu
With pure SQL those are your two real classes of solutions. I like the second better.
(With a stored procedure you could reduce it to a single statement. Something like: TruncateAndPopulate(fubar) But by the time you write the code for TruncateAndPopulate() you'll spend more time than just using the SQL above.)
You could do the truncate after the 'create if not exists'.
That way it will always exist... and always be empty at that point.
CREATE TABLE fubar IF NOT EXISTS
TRUNCATE TABLE fubar
execute any query if table exists.
Usage: call Edit_table(database-name,table-name,query-string);
Procedure will check for existence of table-name under database-name and will execute query-string if it exists.
Following is the stored procedure:
DELIMITER $$
DROP PROCEDURE IF EXISTS `Edit_table` $$
CREATE PROCEDURE `Edit_table` (in_db_nm varchar(20), in_tbl_nm varchar(20), in_your_query varchar(200))
DETERMINISTIC
BEGIN
DECLARE var_table_count INT;
select count(*) INTO #var_table_count from information_schema.TABLES where TABLE_NAME=in_tbl_nm and TABLE_SCHEMA=in_db_nm;
IF (#var_table_count > 0) THEN
SET #in_your_query = in_your_query;
#SELECT #in_your_query;
PREPARE my_query FROM #in_your_query;
EXECUTE my_query;
ELSE
select "Table Not Found";
END IF;
END $$
DELIMITER ;
More on Mysql
how about:
DROP TABLE IF EXISTS fubar;
CREATE TABLE fubar;
Or did you mean you just want to do it with a single query?
OK then, not bad. To be more specific, the current query is doing something like:
$sql1 = "TRUNCATE TABLE fubar";
$sql2 = "CREATE TEMPORARY TABLE IF NOT EXISTS fubar SELECT id, name FROM barfu";
The first time the method containing this is run, it generates an error message on the truncate since the table doesn't exist yet.
Is my only option to do the "CREATE TABLE", run the "TRUNCATE TABLE", and then fill the table? (3 separate queries)
PS - thanks for responding so quickly!
If you're using PHP, use mysql_list_tables to check that the table exists before TRUNCATE it.

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.