SET statement with sp_msforeachdb - sql

does anybody know why this script won't work?
EXECUTE sp_msforeachdb 'USE ?
IF DB_NAME() NOT IN(''master'',''msdb'',''tempdb'',''model'',''ReportServer'')
ALTER DATABASE ? SET AUTO_CLOSE OFF'
It executes fine, but does not exclude master and tempdb.
So the result is this:
Option 'AUTO_CLOSE' cannot be set in database 'master'.
Option 'AUTO_CLOSE' cannot be set in database 'tempdb'.

No idea, but sp_msforeachdb is undocumented and famously unreliable. Use something else instead, eg A reliable and flexible replacement for sp_MSforeachdb.

Related

Customizable database names and TempDB

I have a lump of SQL that looks a little like this
IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = '{FOO}')
BEGIN
EXECUTE ('CREATE DATABASE {FOO}')
ALTER DATABASE {FOO} SET AUTO_CLOSE OFF
END
{FOO} is replaced at runtime with the name of a user configurable database. The logic is that I don't want to create the database if it already exists.
If {FOO} is tempdb then I get a failure when the query runs
Option 'AUTO_CLOSE' cannot be set in database 'tempdb'.
My question is why do I get this failure? SELECT * FROM sys.databases WHERE name = 'tempdb' returns zero results so surely my whole BEGIN/END pair shouldn't run? Indeed, if I put a print statement between begin and end, I don't see any output.
My guess is that SQL Server is doing some kind of linting on the SQL to make sure I don't muck around with tempdb? I have solved the problem by using EXECUTE instead, but I'm a little confused why I have to!
Try ensuring both commands are separate and within dynamic SQL, then the change to tempdb won't be caught by the parser:
EXEC sp_executesql N'CREATE DATABASE {FOO};';
EXEC sp_executesql N'ALTER DATABASE {FOO} SET AUTO_CLOSE OFF;';
This is similar to the reason you can't do this:
IF 1 = 1
BEGIN
CREATE TABLE #t1(id INT);
END
ELSE
BEGIN
CREATE TABLE #t1(x NVARCHAR(255));
END
Even though you and I know that only one of those #t1 code paths will ever be reached, SQL Server presumes that both paths could be reached at runtime, and so complains at parse time.

How to pass a database name as a parameter in SQL Server

USE master
GO
DECLARE #DbName nvarchar(MAX)
SET #DbName = N'DataBase'
ALTER DATABASE #DbName
SET SINGLE_USER WITH ROLLBACK IMMEDIATE
ALTER DATABASE #DbName SET OFFLINE WITH NO_WAIT
GO
ALTER DATABASE #DbName SET ONLINE
GO
ALTER DATABASE #DbName
SET MULTI_USER
GO
I know i can use EXEC but it's a bit ugly....
It is impossible to use DB name from variable.
Use Dynamic Querying, even if it is ugly.
You can't use the database name in a variable.
You have several options:
Different DML scripts for each DB
Dynamic SQL
Firstly, you can't parameterise DDL statements like this. Secondly, GO is a batch terminator and parameters won't be available after this.
I don't recall if MSSqlServer allows the same flexibility as Oracle and MySQL, but in those you can set the default database for each connection. If the queries and statements do not specify a database (use (dbname)), it uses the default. Perhaps that is sufficient parametrization for your purposes?

sp_generate_inserts for all the tables? or something similar

I have two databases.
Database A - full of data
Database B - backup of database A, but without data
how can I get all data from database A and just merge it into database B?
My thoughts were to just generate an insert of the whole data or something.
Thanks
Take a look at redgate's SQL Data Compare.
OP said:
database is full of triggers and
constraints
Just restore a complete backup of A as a new database and be done with it. Lots of "one off" inserts created by a script will take forever, play havoc with your transaction log, and most likely fail because of FKs, etc.
That would work if you're trying to do something ongoing, but if you want to do it just once and you have SQL Server Management Studio installed, you can have it do the Export/Import for you. Here's a walk-though with some screenshots:
http://www.databasejournal.com/features/mssql/article.php/3580216/SQL-Server-2005-Import--Export-Wizard.htm
If you don't have any identity fields to worry about, and it's a one-time operation, you can do something like this, which uses sp_msforeachtable and dynamic SQL:
DECLARE #SQL varchar(max)
SET #SQL = '
INSERT INTO DatabaseB.? WITH (TABLOCK)
SELECT *
FROM DatabaseA.?'
exec sp_MSforeachtable #SQL

How do I execute sql text passed as an sp parameter?

I have a stored procedure with an nvarchar parameter. I expect callers to supply the text for a sql command when using this SP.
How do I execute the supplied sql command from within the SP?
Is this even possible?-
I thought it was possible using EXEC but the following:
EXEC #script
errors indicating it can't find a stored procedure by the given name. Since it's a script this is obviously accurate, but leads me to think it's not working as expected.
Use:
BEGIN
EXEC sp_executesql #nvarchar_parameter
END
...assuming the parameter is an entire SQL query. If not:
DECLARE #SQL NVARCHAR(4000)
SET #SQL = 'SELECT ...' + #nvarchar_parameter
BEGIN
EXEC sp_executesql #SQL
END
Be aware of SQL Injection attacks, and I highly recommend reading The curse and blessing of Dynamic SQL.
you can just exec #sqlStatement from within your sp. Though, its not the best thing to do because it opens you up to sql injection. You can see an example here
You use EXECUTE passing it the command as a string. Note this could open your system up to serious vulnerabilities given that it is difficult to verify the non-maliciousness of the SQL statements you are blindly executing.
How do I execute the supplied sql command from within the SP?
Very carefully. That code could do anything, including add or delete records, or even whole tables or databases.
To be safe about this, you need to create a separate user account that only has dbreader permissions on just a small set of allowed tables/views and use the EXECUTE AS command to limit the context to that user.

How do I run SQL queries on different databases dynamically?

I have a sql server stored procedure that I use to backup data from our database before doing an upgrade, and I'd really like it to be able to run the stored procedure on multiple databases by passing in the database name as a parameter. Is there an easy way to do this? The best I can figure is to dynamically build the sql in the stored procedure, but that feels like its the wrong way to do it.
build a procedure to back up the current database, whatever it is. Install this procedure on all databases that you want to backup.
Write another procedure that will launch the backups. This will depend on things that you have not mentioned, like if you have a table containing the names of each database to backup or something like that. Basically all you need to do is loop over the database names and build a string like:
SET #ProcessQueryString=
'EXEC '+DatabaseServer+'.'+DatabaseName+'.dbo.'+'BackupProcedureName param1, param2'
and then just:
EXEC (#ProcessQueryString)
to run it remotely.
There isn't any other way to do this. Dynamic SQL is the only way; if you've got strict controls over DB names and who's running it, then you're okay just truncating everything together, but if there's any doubt use QUOTENAME to escape the parameter safely:
CREATE PROCEDURE doStuff
#dbName NVARCHAR(50)
AS
DECLARE #sql NVARCHAR(1000)
SET #sql = 'SELECT stuff FROM ' + QUOTENAME(#dbName) + '..TableName WHERE stuff = otherstuff'
EXEC sp_ExecuteSQL (#sql)
Obviously, if there's anything more being passed through then you'll want to double-check any other input, and potentially use parameterised dynamic SQL, for example:
CREATE PROCEDURE doStuff
#dbName NVARCHAR(50)
#someValue NVARCHAR(10)
AS
DECLARE #sql NVARCHAR(1000)
SET #sql = 'SELECT stuff FROM ' + QUOTENAME(#dbName) + '..TableName WHERE stuff = #pOtherStuff'
EXEC sp_ExecuteSQL (#sql, '#pOtherStuff NVARCHAR(10)', #someValue)
This then makes sure that parameters for the dynamic SQL are passed through safely and the chances for injection attacks are reduced. It also improves the chances that the execution plan associated with the query will get reused.
personally, i just use a batch file and shell to sqlcmd for things like this. otherwise, building the sql in a stored proc (like you said) would work just fine. not sure why it would be "wrong" to do that.
best regards,
don
MSSQL has an OPENQUERY(dbname,statement) function where if the the server is linked, you specify it as the first parameter and it fires the statement against that server.
you could generate this openquery statement in a dynamic proc. and either it could fire the backup proc on each server, or you could execute the statement directly.
Do you use SSIS? If so you could try creating a couple ssis packages and try scheduling them,or executing them remotely.