Drop databases on a server conditionally from a List - sql-server-2008

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)

Related

Transfer data from MySQL table to a different table

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 !

Cannot get results of a stored procedure into a #TempTable to work [duplicate]

This question already has answers here:
Insert results of a stored procedure into a temporary table
(33 answers)
Closed 9 years ago.
I am using SQL Server 2008 R2 and am trying to get the results of a stored procedure into a temporary table that I can access later on in the calling stored proc. My TSQL is as follows:
CREATE PROCEDURE sp_ToBeCalled AS
(
#SomeParam INT
)
BEGIN
SELECT * FROM tblSomeTable WHERE SomeField = #SomeParam
END
CREATE PROCEDURE sp_CallingProcedure AS
(
#SomeOtherParam INT
)
BEGIN
-- A
SELECT * INTO #MyTempTable FROM sp_ToBeCalled(#SomeOtherParam)
-- B
SELECT * FROM #MyTempTable FOR XML RAW
END
This all compiles fine however when I call sp_CallingProcedure statement -- B returns an error that #MyTempTable.
How can I do "A" so that I can access its results from within a #MyTempTable table without having to declare the structure of #MyTempTable first?
I am looking for a solution that I can use generically. I have a number of existing stored procedures that I need to call from various callers where getting the results queryable is a necessity. I cannot change the existing stored procedures.
I don't want to use
OPENQUOERY() - requires a custom linked server definition
sp_ExecSql() - means I have to build up dynamic SQL which does not give me SP compile time checking.
You are trying to use a Procedure like a tabular function.
Try using
INSERT INTO #MyTempTable (column1, column2...)
exec sp_ToBeCalled(#SomeOtherParam)
A great reference: http://www.sommarskog.se/share_data.html
I managed to partially solve my issue by doing the following:
1) Custom Stored Procedure to select a ROWSET into a global temp table
2) Calling SP calls 1) and then transfers the ##GlobalTempTable into a local #TempTable for processing
This works but has the following "issues":
Potential security risk as "Adhoc Distributed Queries" functionality needs to be turned on
Still requires a Global Temp table that needs to be cleaned up by the caller. Temp table naming is also problematic as multiple 2) will cause an issue.
I include my code below in case it helps someone else. If anyone is able to improve on it please feel free to post.
/* This requires Adhoc Distributed Queries to be turned on:
sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
*/
-- Adapted from: http://stackoverflow.com/questions/653714/how-to-select-into-temp-table-from-stored-procedure
CREATE PROCEDURE [dbo].[ExecIntoTable]
(
#tableName NVARCHAR(256),
#storedProcWithParameters NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #driver VARCHAR(10)
DECLARE #connectionString NVARCHAR(600)
DECLARE #sql NVARCHAR(MAX)
DECLARE #rowsetSql NVARCHAR(MAX)
SET #driver = '''SQLNCLI'''
SET #connectionString =
'''server=' +
CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(256)) +
COALESCE('\' + CAST(SERVERPROPERTY('InstanceName') AS NVARCHAR(256)), '') +
';trusted_connection=yes;Database=' + DB_NAME() + ''''
SET #rowsetSql = '''EXEC ' + REPLACE(#storedProcWithParameters, '''', '''''') + ''''
SET #sql = '
SELECT
*
INTO
' + #tableName + '
FROM
OPENROWSET(' + #driver + ',' + #connectionString + ',' + #rowsetSql + ')'
EXEC (#sql)
END
GO
and then to use in another SP as follows:
EXEC ExecIntoTable '##MyGlobalTable', 'sp_MyStoredProc 13, 1'
SELECT *
INTO #MyLocalTable
FROM ##MyGlobalTable
DROP TABLE ##MyGlobalTable
SELECT * FROM #MyLocalTable

"Looping" through databases with sp_MSforeachdb and returning 1 data set

So, I've been wrestling with the code I found on my buddy's website:
8 Steps to Moving Database Logins
I want to generate the Database Level Security, Roles, and Explicit Permissions statements in one output so I'm not copying and pasting over and over again and so that they run for all databases on the server (minus tempdb of course).
Declare #FullStatement varchar(MAX)
Set #FullStatement = ' use [?]; SELECT dp.state_desc + N'' '' + dp.permission_name + N'' TO '' + cast(QUOTENAME(dpl.name COLLATE DATABASE_DEFAULT) as nvarchar(500)) AS TSQLScript
FROM sys.database_permissions AS dp
INNER JOIN sys.database_principals AS dpl ON (dp.grantee_principal_id = dpl.principal_id)
WHERE dp.major_id = 0
and dpl.name not like ''##%'' -- excluds PBM accounts
and dpl.name not in (''dbo'', ''sa'', ''public'')
ORDER BY dp.permission_name ASC, dp.state_desc ASC'
Exec sp_MSforeachdb #FullStatement
How can I modify what I have, which works as is but is inconvenient, using a Table Variable, Temp Table, etc so all of the statements are in one data set?
David,
Is this what you want?
CREATE TABLE tempdb.dbo.Results (c1 VARCHAR(8000))
Declare #FullStatement varchar(MAX)
Set #FullStatement = 'SELECT ''use [?]''; SELECT dp.state_desc + N'' '' + dp.permission_name + N'' TO '' + cast(QUOTENAME(dpl.name COLLATE DATABASE_DEFAULT) as nvarchar(500)) AS TSQLScript
FROM [?].sys.database_permissions AS dp
INNER JOIN [?].sys.database_principals AS dpl ON (dp.grantee_principal_id = dpl.principal_id)
WHERE dp.major_id = 0
and dpl.name not like ''##%'' -- excluds PBM accounts
and dpl.name not in (''dbo'', ''sa'', ''public'')
ORDER BY dp.permission_name ASC, dp.state_desc ASC'
INSERT INTO tempdb.dbo.Results Exec sp_MSforeachdb #FullStatement
select * FROM tempdb.dbo.Results
There are multiple ways to get this done. You can use powershell to loop through all databases and put the results in excel. Mr Nelson has this on his powershell sql university series. Sorry, would pull the link for you but i am typing on my phone at the airport.
Why are you scripting database users and permissions? They are in the database and will still be there when you migrate the databases. You don't need to do that unless you're recreating the databases from scratch.

Bulk Insert with Named Field Parameter

Through my ASP.NET / SQL Server 2008 app I need to do a bulk insert of records from a CSV file (maybe a million records). I will import them into a staging table first, so I can manipulate some of the data before moving it to a permanent table.
This will happen on a regular basis. And multiple imports may happen simultaneously. I also have to tell each import from the others.
My original plan was to use a column that had an Import_ID in it. But I see that Bulk Insert won't allow me to set a field value.
Doing a search, I see that I can do a Bulk Insert into a view. And I'm guessing that the view can have a named parameter (Import_ID). But I haven't really learned setting up parameters yet, so I don't know if this is possible, or how to do it.
Can someone please tell me how to do this, or let me know another solution?
Thanks
You could bulk insert into a temporary staging table, for example since you know your SPID (and assuming you can trust the schema of some static table) you can say something like this, specifying the #filepath for the CSV file and the #ImportID, creating a table with your session id as a suffix, and doing all your work in a single dynamic SQL batch:
DECLARE #sql NVARCHAR(MAX), #spid VARCHAR(12) = RTRIM(##SPID);
SET #sql = N'SELECT * INTO dbo.Stage' + #spid
+ ' FROM dbo.RealStagingTable WHERE 1 = 0;';
SET #sql += N'BULK INSERT dbo.Stage' + #spid + ' FROM ('''
+ #filepath + ''' WITH (options);'
SET #sql += N'INSERT dbo.RealTable(ImportID, other columns)
SELECT ' + RTRIM(#ImportID) + , other columns
FROM dbo.Stage' + #spid + ';';
SET #sql += N'DROP TABLE dbo.Stage' + #spid + ';'
EXEC sp_executesql #sql;

Drop database in SQL Server using wildcard

I have an application that creates a separate database (SQL Server 2008) for each new customer, during testing we end up with a lot of databases called PREFIX.whatever ...
I would love a script that would look for all databases starting with PREFIX. and drop them so we can start a clean test cycle. Any help greatly appreciated.
SELECT ' DROP DATABASE [' + NAME + ']' FROM sys.sysdatabases where name like 'PREFIX%'
Copy the output and execute this to drop Databases in your criteria. You can also schedule this on a daily basis with a little tweaking.
Update:
We ended up expanding the answer from Baaju so I thought I would share it. We call teh following script from MSBuild and it cleans out all of teh existing DB's created during testing:
use master
DECLARE #Name nvarchar(1000);
DECLARE testdb_cursor CURSOR FOR
SELECT 'ALTER DATABASE' + '[' + NAME + ']' + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE DROP DATABASE ' + '[' + NAME + ']' FROM sys.sysdatabases where name like 'TCM.%'
OPEN testdb_cursor;
-- Perform the first fetch and store the value in a variable.
FETCH NEXT FROM testdb_cursor
INTO #Name;
-- Check ##FETCH_STATUS to see if there are any more rows to fetch.
WHILE ##FETCH_STATUS = 0
BEGIN
-- Concatenate and display the current values in the variables.
exec sp_executesql #Name;
-- This is executed as long as the previous fetch succeeds.
FETCH NEXT FROM testdb_cursor
INTO #Name;
END
CLOSE testdb_cursor;
DEALLOCATE testdb_cursor;
Just ran into this and come up with a slight variation to allow immediate execution without cursors:
DECLARE #SQL NVARCHAR(MAX) = ''
SELECT #SQL = #SQL
+ 'ALTER DATABASE [' + [name] + '] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; '
+ 'DROP DATABASE [' + [name] + ']; '
FROM sys.databases
WHERE [name] like 'temp_%' AND create_date < DATEADD(day,-7,GETDATE())
-- display statements
SELECT #SQL
-- execute (uncomment)
--EXEC sp_executesql #SQL
The above is deleting any databases starting with "temp_" and older than 7 days, but that can be adapted obviously to any situation.
DANGER: Mess up your query, delete some or all of your databases. I left the EXEC statement commented out just to try to avoid someone doing doing this through copy/paste.