Run long SQL script for each database - sql

I have a long SQL script i'd like to run for each of my databases on one server. What's the best way to do that?
I found this in my research:
EXEC sp_msforeachdb "
IF '?' IN ('lib1','lib2','lib3')
BEGIN
use ?;
exec 'my_sp_long_sql_script'
END
"
I tried it but it needed me to create the sp in each library, which kind of defeats the purpose of the loop. Or how do I automate creating a sp for each library?
All I want to do is run my long_sql_script for each of my databases.

Use the fully qualified name of the stored procedure instead of its relative name, to retrieve it from whereever it is defined. This is how SQL Server itself works.

Related

Loop Through All SSMS Databases without Recreating Stored Procedure

Background Information:
In Python, I might write something like this if I want to apply the same logic to different values in a list.
database_list = ["db_1", "db_2", "db_3"]
for x in range(0,len(database_list),1):
print("the database name is " + database_list[x])
What I am trying to do:
What I am trying to do in SSMS, is pull a list of DB objects for each database. I created a stored procedure to pull exactly what I want, but I have to run it against each database, so 10 databases mean running it 10 times.
My goal is to do this with a T-SQL query instead of Python.
I tried doing something like this:
exec sp_MSforeachdb 'USE ?; EXEC [dbo].[my_stored_procedure]';
The problem with this is, [dbo].[my_stored_procedure] has to exist in every database I want to do this in.
How can I create the stored procedure in 1 database, but execute it for all databases or a list of databases that I choose?
I know what you are trying to do and if it's what I think (you seem reluctant to actually say!) you can do the following:
In the master database, create your procedure. Normally you wouldn't do this, but in this case you must prefix it sp_
use master
go
create procedure sp_testproc as
select top 10 * from sys.tables
go
Now if you run this, it will return tables from the master database.
If you switch context to another database and exec master.dbo.sp_testproc, it will still return tables from the master database.
In master, run
sys.sp_MS_marksystemobject sp_testproc
Now switch context to a different database and exec master.dbo.sp_testproc
It will return tables from the database you are using.
Try creating your sproc in master and naming it with an sp_ prefix:
USE master
GO
CREATE PROCEDURE sp_sproc_name
AS
BEGIN
...
END
GO
-- You *may* need to mark it as a system object
EXEC sys.sp_MS_marksystemobject sp_sprocname
See: https://nickstips.wordpress.com/2010/10/18/sql-making-a-stored-procedure-available-to-all-databases/
It should then be available in all dbs
Create the stored procedure in the Master database with the sp_ prefix, and use dynamic SQL in the stored procedure so it resolves object names relative to the current database, rather than the database which contains the stored procedure.
EG
use master
go
CREATE OR ALTER PROCEDURE [dbo].[sp_getobjects]
AS
exec ('
select *
from [sys].[objects]
where is_ms_shipped = 0
order by type, name
')
go
use AdventureWorks2017
exec sp_getobjects
#LunchBox - it's your single stored procedure (that you create in one database) that is actually going to need to contain the "exec sp_MSforeach ...." command, and instead of the command to be executed being "EXEC ", it will need to be the actual SQL that you were going to put into the stored proc.
Eg. (inside your single stored procedure)
EXEC sp_MSforeachdb 'USE ?; SELECT * FROM <table>; UPDATE <another table> SET ...';
Think of the stored procedure (that you put into one database) as being no different than your Python code file - if you had actually wanted to achieve the same thing in Python, you would have either needed to create the stored proc in each database, or build the SQL statement string in Python and execute it against each database.
I understand what you thought you might be able to achieve with SQL, but stored procedures really don't work the way you were expecting. Even when you're in the context of a different database, but you run EXEC <different_db>.stored_proc, that stored proc ends up running in the context of the database in which it exists (not your context database).
Now, the only one issue you may come up against is that the standard sp_MSforeachdb stored proc has a limit of 2000 characters for the command that can be executed (although, it does have multiple "command" parameters, this may not be practical if you were planning on running a very large code block, perhaps with variables that carry all the way through). If this is something that might impact what you're intending to do, you could do a search online for "sp_MSforeachdb alternatives" - there seem to be a handful that people have created where the command parameter can contain a larger string.

Validating a Dynamic SQL DELETE statement without executing the statement [duplicate]

I have access to an Access database and within that database are fields filled with TSQL queries. These queries are processed by T-SQL on a server. So when I write these SQL queries and put them into a field for use by the end server, I'm unable to validate the syntax/etc. I could create a temporary query in that Access database, but it's not the same query language. For example, Access would correctly use IIF but TSQL would not (it would instead be CASE).
I don't have direct access to this server with TSQL, is there a way I can validate my T-SQL queries (for syntax and the like)? Perhaps a web tool online?
I should note I do not have access to the SQL server. Only the Access db and that alone. I understand it will not validate table names and the like, I wouldn't expect it to.
Actually, a combination of MattMc3's answer and FremenFreedom's answer should work.
Download SQL Express.
Then, declare the following stored procedure:
create procedure IsValidSQL (#sql varchar(max)) as
begin
begin try
set #sql = 'set parseonly on;'+#sql;
exec(#sql);
end try
begin catch
return(1);
end catch;
return(0);
end; -- IsValidSQL
You can test it with:
declare #retval int;
exec #retval = IsValidSQL 'select iif(val, 0, 1) from t';
select #retval
or with:
declare #retval int;
exec #retval = IsValidSQL 'select val from t';
select #retval
Note: this will catch the IIF() issue. It will not catch anything related to the table structures or column structures. You would need the schema for that and a slightly different approach ("select top 0 * from () t") woudl do it.
You might be able to do something with SQL Fiddle online. However, I would suggest having a local copy of the database.
You can parse your T-SQL to check for valid syntax by executing it on the SQL Server machine with a SET PARSEONLY ON as the first line of your script. It will not validate table or field names, but will provide you with any syntax errors.
The Data Dude (Gert Drapers) describes how to use the built-in SQL Server T-SQL parser in your application here:
Getting to the Crown Jewels
If you want to only check the validity of the SQL statements that you have - this might be a nice way to go, and it doesn't require SQL Server per se to be installed where you run your unit tests.
It's a .NET based approach, and it cannot - of course - validate object names in your database if you're not using a live database - but it can catch syntactical errors in your T-SQL statements.
You can use the NOEXEC option:
SET NOEXEC ON
SELECT 1 AS Test
SET NOEXEC OFF
Is SQL Server Management Studio Express (free download) able to connect to regular SQL Server instances? If so, perhaps you could test the queries there. Even if you could not connect to the actual server, you might be able to create a test version of your database in Express that would at least allow you to catch syntax and naming problems.
If they are fairly static, convert them into stored procedures in the Sql Database and then just call them from access.

T-SQL equivalent of GO

I'm trying to write a T-SQL script to create a database and the corresponding tables. I'm having a problem where the USE statement complains that the database that I just "created" doesn't exist. If I run the script within SQL Server Management Studio so that I can make use of the GO statement, I don't get this issue.
Is there a T-SQL equivalent of GO that I can use to make sure the CREATE DATABASE gets executed before the USE?
I've tried BEGIN/COMMIT TRANSACTION and BEGIN/END but they didn't help.
Is there a T-SQL equivalent of GO that I can use to make sure the CREATE DATABASE gets executed before the USE?
Yes. Dynamic SQL. Each dynamic SQL invocation is a parsed, compiled, and executed as a separate batch.
EG:
exec ('
create database foo
')
exec ('
use foo
create table bar(id int)
')
Note that when used in dynamic SQL use database only change the database context for the dynamic batch. When control returns to the calling batch, the database context is restored.
In C# you should use separate calls to SqlComand for each batch.
High level steps.
Open connection to master.
Create new database (just create database statement).
Instead of USE call SqlConnection.ChangeDatabase(String) Method
Execute remaining batches

Ways to validate T-SQL queries?

I have access to an Access database and within that database are fields filled with TSQL queries. These queries are processed by T-SQL on a server. So when I write these SQL queries and put them into a field for use by the end server, I'm unable to validate the syntax/etc. I could create a temporary query in that Access database, but it's not the same query language. For example, Access would correctly use IIF but TSQL would not (it would instead be CASE).
I don't have direct access to this server with TSQL, is there a way I can validate my T-SQL queries (for syntax and the like)? Perhaps a web tool online?
I should note I do not have access to the SQL server. Only the Access db and that alone. I understand it will not validate table names and the like, I wouldn't expect it to.
Actually, a combination of MattMc3's answer and FremenFreedom's answer should work.
Download SQL Express.
Then, declare the following stored procedure:
create procedure IsValidSQL (#sql varchar(max)) as
begin
begin try
set #sql = 'set parseonly on;'+#sql;
exec(#sql);
end try
begin catch
return(1);
end catch;
return(0);
end; -- IsValidSQL
You can test it with:
declare #retval int;
exec #retval = IsValidSQL 'select iif(val, 0, 1) from t';
select #retval
or with:
declare #retval int;
exec #retval = IsValidSQL 'select val from t';
select #retval
Note: this will catch the IIF() issue. It will not catch anything related to the table structures or column structures. You would need the schema for that and a slightly different approach ("select top 0 * from () t") woudl do it.
You might be able to do something with SQL Fiddle online. However, I would suggest having a local copy of the database.
You can parse your T-SQL to check for valid syntax by executing it on the SQL Server machine with a SET PARSEONLY ON as the first line of your script. It will not validate table or field names, but will provide you with any syntax errors.
The Data Dude (Gert Drapers) describes how to use the built-in SQL Server T-SQL parser in your application here:
Getting to the Crown Jewels
If you want to only check the validity of the SQL statements that you have - this might be a nice way to go, and it doesn't require SQL Server per se to be installed where you run your unit tests.
It's a .NET based approach, and it cannot - of course - validate object names in your database if you're not using a live database - but it can catch syntactical errors in your T-SQL statements.
You can use the NOEXEC option:
SET NOEXEC ON
SELECT 1 AS Test
SET NOEXEC OFF
Is SQL Server Management Studio Express (free download) able to connect to regular SQL Server instances? If so, perhaps you could test the queries there. Even if you could not connect to the actual server, you might be able to create a test version of your database in Express that would at least allow you to catch syntax and naming problems.
If they are fairly static, convert them into stored procedures in the Sql Database and then just call them from access.

How to query results from another query?

I am writing a Trans-SQL script against a MSSQL 2005 Server that intends to query file path of each database present. I am able to list out the database present in the system. But how do I run a separate query based on the results?
The following is the output from the list of databases using the command (SELECT name from sys.databases):
name
----
master
tempdb
model
msdb
Now i would like to take this database names (e.g. master, tempdb) and enter into another query namely (exec sp_helpdb <database_name>).
any ideas?
Not answering your question directly, but if you want to run a query for each db, you can use sp_msforeachdb.
sp_msforeachdb 'EXEC sp_helpdb [?]'
Otherwise, you're going to need to use the results to generate your SQL.
You can build a cursor based on that query then loop through the results, stuff them into a SQL variable, and use that variable to exec your sproc. Unfortunately I'm not able to give you a sample right now, but that is the way I would approach it.
In general, the answer to your question would be "use a subquery".
But in this case, you're using a SQL Server stored procedure. So the best approach is to write your own stored procedure to:
1) call sp_helpdb (or select from master..sysdatabases)
2) Iterate through the results
Here's an example:
http://www.mssqltips.com/sqlservertip/1070/simple-script-to-backup-all-sql-server-databases/
If i am understanding correct , you can use derived table here:-
select database.name (your query) from
(SELECT name from sys.databases) database