I have a database, let's simply call it 'db', on my computer, with a few tables that have multiple columns and data inside those tables.
I have a software using this database to store configuration elements and some other stuff.
Now, I am releasing a new version of my software, with only slight modifications in the database, i.e. some columns may have been added to tables, or removed (but no column renamed).
I must keep all data, so I would like to transfer it to the new "version" of my database.
What I thought of :
Rename 'db' into 'db_old'.
Install the new database as 'db_new', with the default values in the new columns
For each table, get a list of all the columns from 'db_old' that are present in 'db_new'
Use a INSERT INTO ... SELECT to put that old stuff back into 'db_new'.
drop the old db and use my new db.
Do you think it can work ? Do you have any easy solution ?
Also, I'm absolutely not an SQL expert... And I tried this (without looking if the column has been removed or not yet) :
SELECT
GROUP_CONCAT(COLUMN_NAME
SEPARATOR ',')
INTO #colList FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_SCHEMA = 'db_old'
AND TABLE_NAME = 'configuration';
INSERT
INTO db_new.configuration (SELECT #colList)
SELECT #colList FROM
db_old.configuration;
But it fails on replacing the second #colList by the effective list... Can you also help me on this issue ?
Thank you everyone and have a nice day !
You should first take a dump of your DB Database and create a .sql file. Depending upon on your DB Data, this file can even go in GBs. This SQL File will contain all your tables and all the data inside those tables. I will suggest you open and see the file.
Then you should use this new created file and use it to import all the data into new DB. It will put all those tables, data into this new DB.
Here is how to do that. First create SQL file:
mysqldump -h [SeverIpAddress] -u [UserName] -p[password] YourDbname > db_backup.sql
Use -h [SeverIpAddress] in case of Remote severs. In case, it resdies in your own system, you don't need to use this.
Then You should create your new DB, lets say DB_new. once created, switch to it using use command.
use DB_new
Once done, now import your .SQl file that we have created before using source command.
source YourSQLFilePath
In your case, source db_backup.sql
OK. If anyone ever encounter the same problem, here is the solution.
First, admit you have a database called 'myDatabase', with a table called 'myTable' that you want to "upgrade", i.e. you want to modify the table structure by adding/removing columns but keep the data inside.
First step is to drop foreign keys (if any) and to rename "myTable" :
USE `myDatabase`;
ALTER TABLE `myTable` DROP FOREIGN KEY `my_fk_constraint`;
ALTER TABLE `myTable` RENAME TO `old_myTable`;
Second step is to import the new table structure, by using SOURCE for example.
SOURCE C:/new_table_structure.sql
Third step is optional, but you may need this if your table has a lot of columns :
USE `myDatabase`;
SET GLOBAL group_concat_max_len = 4294967295;
Fourth step is to store the following routine :
delimiter //
DROP PROCEDURE IF EXISTS updateConf//
CREATE PROCEDURE updateConf(IN dbName TEXT, IN old_table TEXT, IN new_table TEXT, IN primary_key_name TEXT)
BEGIN
-- get column count in old table
SELECT count(*)
INTO #colNb
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = dbName
AND TABLE_NAME = old_table;
-- get string with all column names from old_table
SELECT GROUP_CONCAT(COLUMN_NAME)
INTO #colNames1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = dbName
AND TABLE_NAME = old_table;
SET #colNames1 = CONCAT(#colNames1, ',');
-- get string with all column names from new_table
SELECT GROUP_CONCAT(COLUMN_NAME)
INTO #colNames2
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = dbName
AND TABLE_NAME = new_table;
-- variables initialization
SET #cpt = 1; -- column number counter
SET #pos = 1; -- position of column name first char
SET #vir = 1; -- next comma position
-- start of loop
label: LOOP
IF #cpt <= #colNb THEN
SET #vir = LOCATE(',',#colNames1,#pos); -- localize next comma
SET #colName = SUBSTRING(#colNames1, #pos, #vir - #pos); -- get column name
SET #pos = #vir + 1; -- update next column position
-- if column is in both tables
IF FIND_IN_SET(#colName, #colNames2) AND #colName != primary_key_name THEN
SET #execut = CONCAT("INSERT INTO ", new_table, " (", primary_key_name, ",", #colName, ") SELECT ", primary_key_name, ",", #colName, " FROM ", old_table, " ON DUPLICATE KEY UPDATE ", new_table, ".", #colName, " = ", old_table, ".", #colName);
PREPARE stmt FROM #execut;
EXECUTE stmt;
END IF;
SET #cpt = #cpt + 1; -- counter increment
-- when all columns parsed
ELSE
LEAVE label; -- end of loop
END IF;
END LOOP label;
END //
delimiter ;
Final step is to call the procedure on tables, and to drop the temporary table:
CALL updateConf( 'myDatabase', 'old_myTable', 'myTable', 'primaryKeyName' );
DROP TABLE `old_myTable`;
And voila ! Just don't forget to put back the foreign keys you dropped :)
It surely can be done in better ways, but i got this to work correctly.
Thank you everyone !
Related
I am programming MYSQL and I use Python on Raspberry PI 4.
I need to drop table when all the values in my status_s column are equal to "DONE". I cannot figure out how to drop table under a certain condition. MYSQL tables can be found here for testing:
https://www.db-fiddle.com/f/siZmmKWLjRDdpYX6deEPYF/1
Initially, the status_s values are not "DONE". As my program runs, the values update and eventually all of them will be "DONE", at that point, I do not want to have this table anymore as it is not important.
Thanks in advance
UPDATE Adding snippet of Python program
def update_data_when_complete(conn,table_name):
cur = conn.cursor()
sql = "SELECT COUNT(DISTINCT(ID)) = SUM(Status = 'DONE') FROM {table}"
cur.execute(sql.format(table=table_name))
complete_result = cur.fetchone()
conn.commit()
#print("COmplete result = ",complete_result[0])
# if complete_result[0] is 1 here, all rows are "DONE" and must delete table after few minutes
if(complete_result[0] == 1):
sql = "DROP TABLE {table}"
cur.execute(sql.format(table=table_name))
conn.commit()
else:
print("Table not fully complete yet")
Use Event Scheduler.
Create event procedure:
CREATE EVENT remove_temptable
ON SCHEDULE
EVERY 1 MINUTE
COMMENT 'Remove `temptable` when its `status_s` column is equal to "DONE" in all rows.'
DO
BEGIN
IF EXISTS ( SELECT NULL
FROM INFORMATOIN_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'my_database'
AND TABLE_NAME = 'temptable' ) THEN
IF !( SELECT SUM(status_s != 'DONE')
FROM my_database.temptable ) THEN
DROP TABLE my_database.temptable;
END iF;
END IF;
END;
This procedure will check the table temptable for its existence firstly. If it exists then the procedure checks does a row with non-NULL value in status_s column other than 'DONE' exists. If not then the procedure drops the table.
The procedure is executed each minute. You may adjust how often it is executed. Also, when it is created, you may enable or disable it using ALTER EVENT (for example you may enable it after temptable creation and disable after you ensure the table is dropped).
Do not forget to enable Event Scheduler.
I'm trying to update a column (in this case, a date) that is present on most of the tables on my database. Sadly, my database has more than 100 tables already created and full of information. Is there any way to loop through them and just use:
UPDATE SET date = '2016-04-20' WHERE name = 'Example'
on the loop?
One painless option would be to create a query which generates the UPDATE statements you want to run on all the tables:
SELECT CONCAT('UPDATE ', a.table_name, ' SET date = "2016-04-20" WHERE name = "Example";')
FROM information_schema.tables a
WHERE a.table_schema = 'YourDBNameHere'
You can copy the output from this query, paste it in the query editor, and run it.
Update:
As #PaulSpiegel pointed out, the above solution might be inconvenient if one be using an editor such as HeidiSQL, because it would require manually copying each record in the result set. Employing a trick using GROUP_CONCAT() would give a single string containing every desired UPDATE query in it:
SELECT GROUP_CONCAT(t.query SEPARATOR '; ')
FROM
(
SELECT CONCAT('UPDATE ', a.table_name,
' SET date = "2016-04-20" WHERE name = "Example";') AS query,
'1' AS id
FROM information_schema.tables a
WHERE a.table_schema = 'YourDBNameHere'
) t
GROUP BY t.id
You can use SHOW TABLES command to list all tables in database. Next you can check if column presented in table with SHOW COLUMNS command. It can be used this way:
SHOW COLUMNS FROM `table_name` LIKE `column_name`
If this query returns result, then column exists and you can perform UPDATE query on it.
Update
You can check this procedure on sqlfiddle.
CREATE PROCEDURE UpdateTables (IN WhereColumn VARCHAR(10),
IN WhereValue VARCHAR(10),
IN UpdateColumn VARCHAR(10),
IN UpdateValue VARCHAR(10))
BEGIN
DECLARE Finished BOOL DEFAULT FALSE;
DECLARE TableName VARCHAR(10);
DECLARE TablesCursor CURSOR FOR
SELECT c1.TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS c1
JOIN INFORMATION_SCHEMA.COLUMNS c2 ON (c1.TABLE_SCHEMA = c2.TABLE_SCHEMA AND c1.TABLE_NAME = c2.TABLE_NAME)
WHERE c1.TABLE_SCHEMA = DATABASE()
AND c1.COLUMN_NAME = WhereColumn
AND c2.COLUMN_NAME = UpdateColumn;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Finished = TRUE;
OPEN TablesCursor;
MainLoop: LOOP
FETCH TablesCursor INTO TableName;
IF Finished THEN
LEAVE MainLoop;
END IF;
SET #queryText = CONCAT('UPDATE ', TableName, ' SET ', UpdateColumn, '=', QUOTE(UpdateValue), ' WHERE ', WhereColumn, '=', QUOTE(WhereValue));
PREPARE updateQuery FROM #queryText;
EXECUTE updateQuery;
DEALLOCATE PREPARE updateQuery;
END LOOP;
CLOSE TablesCursor;
END
This is just an example how to iterate through all tables in database and perform some action with them. Procedure can be changed according to your needs.
Assuming you are using MySQL, You can use Stored Procedure.
This post is a very helpful.
Mysql-loop-through-tables
I'm looking for a straight way to run a query on all databases hosted on my mysql server.
I have a bunch of Magento installations and I want to truncate all Magento log table on all databases:
log_customer
log_visitor
log_visitor_info
log_url
log_url_info
log_quote
report_viewed_product_index
report_compared_product_index
report_event
catalog_compare_item
I think it something very easy to accomplish in mysql but I cannot find a straight answer/solution.
*UPDATE *
According to #Ollie Jones it is not possible to do it without a STORE PROCEDURE or a server side language ( PHP or whatever )
UPDATE 1
I choose to follow the PHP approach (#samitha) for 2 reasons:
STORE PROCEDURE looks more complicated
Query on 'information_schema' table is very slow ( at least if you have many DB/TABLES)
SELECT DISTINCT SCHEMA_NAME AS `database`
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql')
ORDER BY SCHEMA_NAME
gets you a list of all the non-MYSQL databases on your system.
SELECT TABLE_SCHEMA AS `database`,
TABLE_NAME AS `table`
FROM information_schema.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
ORDER BY TABLE_SCHEMA, TABLE_NAME
gets you a list of all the actual tables (excluding SYSTEM VIEWs like the TABLES table, and user-defined views) in all the databases.
Then, you should implement logic in your program to ensure that, for each database, it really is a Magento database before you truncate certain tables. Otherwise, you might become a despised person among your co-workers. :-)
Edit
Here's a stored procedure.
You need to edit it to do exactly what you need it to do; in particular, it counts rows rather than truncating tables, and it doesn't contain the correct list of log tables. (It would be irresponsible for me to publish such a wildly destructive stored procedure; you should edit it yourself to do the destructive part.)
DELIMITER $$
DROP PROCEDURE IF EXISTS `zap_magento_logs`$$
CREATE PROCEDURE `zap_magento_logs`()
BEGIN
-- declare variables for database and table names
DECLARE dbname VARCHAR(128) DEFAULT '';
DECLARE tbname VARCHAR(128) DEFAULT '';
DECLARE done INTEGER DEFAULT 0;
-- declare cursor for list of log tables
DECLARE log_table_list CURSOR FOR
SELECT TABLE_SCHEMA AS `database`,
TABLE_NAME AS `table`
FROM `information_schema`.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME IN
(
'log_customer',
'log_visitor',
'log_visitor_info',
'log_url',
'log_url_info',
'log_quote'
)
ORDER BY TABLE_SCHEMA, TABLE_NAME;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET done = 1;
OPEN log_table_list;
log_table: LOOP
FETCH log_table_list INTO dbname, tbname;
IF done = 1 THEN
LEAVE log_table;
END IF;
-- create an appropriate text string for a DDL or other SQL statement
SET #s = CONCAT('SELECT COUNT(*) AS num FROM ',dbname,'.',tbname);
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP log_table;
CLOSE log_table_list;
END$$
DELIMITER ;
You run this by issuing the SQL command
CALL zap_magento_logs();
A PHP approach would be:
$tables = array(
'log_customer',
'log_visitor',
'log_visitor_info',
'log_url',
'log_url_info',
'log_quote',
'report_viewed_product_index',
'report_compared_product_index',
'report_event',
'catalog_compare_item',
);
$dbh = new PDO('mysql:host=localhost;', 'USERNAME', 'PASSWORD', array(
PDO::ATTR_PERSISTENT => true
));
$sql = $dbh->query('SHOW DATABASES');
$getAllDbs = $sql->fetchALL(PDO::FETCH_ASSOC);
foreach ($getAllDbs as $DB) {
foreach ($tables as $table) {
$dbh->query('TRUNCATE TABLE ' . $DB['Database'] . '.' . $table);
};
};
I didn't feel like writing code to solve this so I found a different solution. I wrote SQL that generates the SQL that I need. So I saved the following to a file called createSomeSQL.sql:
SET sql_mode='PIPES_AS_CONCAT';
select
'truncate table ' || dbs.database || '.someLogTable;'
as ''
from (SELECT DISTINCT SCHEMA_NAME AS `database`
FROM information_schema.SCHEMATA
WHERE SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql', 'test')
ORDER BY SCHEMA_NAME) as dbs;
You could replace the SQL in line 4 with anything you want. Then I ran this command to generate the SQL that I need:
mysql -u root -p < createSomeSQL.sql > sqlToExecute.sql
Replace "root" with your username, of course. Now the file sqlToExecute.sql contains a script you can run to execute that SQL against all your databases.
Try the following (very basic, no error handling, may not work at all, I've not tested this):
$db = mysqli_connect(); // your database connection
$tables = ["log_customer", "log_visitor", "log_visitor_info"]; // array with all the tables
foreach ($tables as $table) {
mysqli_query($db, "TRUNCATE TABLE `".$table."`"); // executes query for each element in the array
}
I have created a list of databases I want to drop based on a set of conditions and have titled it delete_table in the master database (this already excludes master, tempdb, model and msdb). The only column in this table is name which contain the exact database names that should be deleted.
How would I go about writing the script that would drop the databases on the server based on this list?
Thanks!
If you want a quick way to script out DROP DATABASE commands:
--set database in single user mode, rolling back active transactions...be careful!
SELECT 'ALTER DATABASE ' + QUOTENAME(name) + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE;' FROM sys.databases WHERE name IN (...)
UNION
--drop database
SELECT 'DROP DATABASE ' + QUOTENAME(name) + ';' FROM sys.databases WHERE name IN (...);
Replace ... with a comma separated list of database names (e.g N'DatabaseOne', 'DatabaseTwo', etc.). Run the select query, then use results for execution.
I don't understand the need for a user table to solve this. As a side note, you should avoid creating user objects in master database. Granted, I can't think of a reason other than one based on aesthetics, but it just seems wrong.
More on user-created objects in system databases...
MSDN states user objects shouldn't be created in master, but I think the reason provided is pretty weak. A more substantive argument involves lack of control over what happens to system database objects during service pack/version upgrades.
I'm not saying the next service pack upgrade will wipe out your user created objects in master, but who knows. Put your utility and administration-type objects in a user created database so there's no confusion.
quick way
DECLARE #SQL VARCHAR(MAX) = ''
SELECT #SQL = #SQL+ 'DROP DATABASE ' + QUOTENAME(name) + ';'
FROM delete_table
EXECUTE (#SQL)
a bit safer way to make sure database exists and each database get scripted only once incase you have dups
DECLARE #SQL VARCHAR(MAX) = ''
SELECT #SQL = #SQL+ 'DROP DATABASE ' + QUOTENAME(name) + ';'
FROM sys.databases WHERE name IN (SELECT name FROM delete_table);
EXECUTE (#SQL)
Edit to add alter database simply add this line
DECLARE #SQL VARCHAR(MAX) = ''
SELECT #SQL = #SQL+
'ALTER DATABASE '+ QUOTENAME(name) + 'SET SINGLE_USER WITH ROLLBACK IMMEDIATE;' +
'DROP DATABASE ' + QUOTENAME(name) + ';'
FROM sys.databases WHERE name IN (SELECT name FROM delete_table);
EXECUTE (#SQL)
I do have a table with more than 100000 data elements, but there are almost 350 blank rows within. How do I delete this blank rows using phpmyadmin? Manually deleting is a tedious task.
The general answer is:
DELETE FROM table_name WHERE some_column = '';
or
DELETE FROM table_name WHERE some_column IS NULL;
See: http://dev.mysql.com/doc/refman/5.0/en/delete.html
More info when you post your tables!~
Also, be sure to do:
SELECT * FROM table_name WHERE some_column = '';
before you delete, so you can see which rows you are deleting! I think in phpMyAdmin you can even just do the select and then "select all" and delete, but I'm not sure. This would be pretty fast, and very safe.
I am doing the mysql operation in command prompt in windows. And the basic queries:
delete * from table_name where column=''
and
delete * from table_name where column='NULL'
doesn't work. I don't know whether it works in phpmyadmin sqlcommand builder. Anyway:
delete * from table_name where column is NULL
works fine.
I have a PHP script that automatically removes empty rows based on column data types.
That allows me to define "emptiness" differently for different column types.
e.g.
table
first_name (varchar) | last_name (varchar) | some_qty ( int ) | other_qty (decimal)
DELETE FROM `table` WHERE
(`first_name` IS NULL OR `first_name` = '')
AND
(`last_name` IS NULL OR `last_name` = '')
AND
(`some_qty` IS NULL OR `some_qty` = 0)
AND
(`other_qty` IS NULL OR `other_qty` = 0)
Since "0" values are meaningless in my system, I count them as empty. But I found out that if you do (first_name = 0) then you will always get true, because strings always == 0 in MySQL. So I tailor the definition of "empty" to the data type.
This procedure will delete any row for all columns that are null ignoring the primary column that may be set as an ID. I hope it helps you.
DELIMITER //
CREATE PROCEDURE DeleteRowsAllColNull(IN tbl VARCHAR(64))
BEGIN
SET #tbl = tbl;
SET SESSION group_concat_max_len = 1000000;
SELECT CONCAT('DELETE FROM `',#tbl,'` WHERE ',(REPLACE(group_concat(concat('`',COLUMN_NAME, '` is NULL')),',',' AND ')),';') FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = #tbl AND COLUMN_KEY NOT LIKE 'PRI' into #delete_all;
PREPARE delete_all FROM #delete_all;
EXECUTE delete_all;
DEALLOCATE PREPARE delete_all;
END //
DELIMITER ;
Execute the procedure like this.
CALL DeleteRowsAllColNull('your table');
I know this has already been answered and has got a tick, but I wrote a small function for doing this, and thought it might be useful to other people.
I call my function with an array so that I can use the same function for different tables.
$tableArray=array("Address", "Email", "Phone"); //This is the column names
$this->deleteBlankLines("tableName",$tableArray);
and here is the function which takes the array and builds the delete string
private function deleteBlankLines($tablename,$columnArray){
$Where="";
foreach($columnArray as $line):
$Where.="(`".$line."`=''||`".$line."` IS NULL) && ";
endforeach;
$Where = rtrim($Where, '&& ');
$query="DELETE FROM `{$tablename}` WHERE ".$Where;
$stmt = $this->db->prepare($query);
$stmt->execute();
}
You can use this function for multiple tables. You just need to send in a different table name and array and it will work.
My function will check for a whole row of empty columns or NULL columns at the same time. If you don't need it to check for NULL then you can remove that part.