Copying a stored procedure from one database to another - sql

I have a central management database which collates some information and runs some dynamic SQL for various other tasks when a new database is restored into the environment. One of those tasks is going to be a bit complex to achieve through dynamic SQL so I had the idea of creating a master copy stored procedure in the central DB and copying that over to the new databases after they are restored.
I've seen a few examples of people trying to do that on here but I can't get anything to play ball.
Here's what i am trying to achieve conceptually, note that I'm trying to cater for potentially multiple stored procedures to be created in this way just for future proofing.
declare #sql nvarchar(max), #DatabaseName nvarchar(200)
set #DatabaseName = 'TargetDatabase'
set #sql =
(
SELECT definition + char(13) + 'GO'
FROM sys.sql_modules s
INNER JOIN sys.procedures p
ON [s].[object_id] = [p].[object_id] WHERE p.name LIKE '%mastercopy%'
)
exec #sql
Thanks

Instead of creating dynamic script you could use one script with all the procedures that you want to create (you can script all the procs you want using 2 click in SSMS), you then run this script manually in the context of the database where you want to create these procedures or by passing the file with this script to sqlcmd with -i and passing the correct database name with -d.
Here Use the sqlcmd Utility you can see the examples.

Related

SQL Server variable DB as stored procedure parameter

I have an Application Database (A-DB) which imports from a set of Databases (B-set-DBs, (around 50 DBs)).
The user selects 4 DBs of "B-set-DB`s" from which he wannts to import into the A-DB for his application.
I wrote an stored procedure for the import (about 4000 Lines of SQL Code) with 4 hard coded DB`s of B-set-DBs and it works perfect.
So my question is how to make the imported Database names variable?
I can not use dynamic SQL for the whole 4000 lines, because im callin functions and other stored procedures, there are a lot of parameters and strings and definitions of temporary tables and supqueries of subqueries of sub......and a lot of other weird things.
(I know it all sounds ugly but i just have to get it to run, i did not design the whole thing.)
Maybe it is possible to define a synonyme at the first line of the import stored procedure:
ALTER PROCEDURE [dbo].[test_storedProcedure]
(#DBName_1 nvarchar(512), #table_name_1 nvarchar(512))
AS
BEGIN
DECLARE #cmd nvarchar(max);
set #cmd = N'create synonym DB_SYN' +
N' for ' +
#DBName_1 + --[A70_V70_V280]
#table_name_1 --.[dbo].[UNITCHANGEMAP]
exec sp_executesql #cmd
...
SELECT * INTO A-DB
FROM DB_SYN
...
END
And afterwards work only with the Synonyms.
Or does anyone knows a way which will do the same?
BTW I am using SQL Server 2008
So i see all the 50 Databases in the Object Explorer of SQL Server.

Alter or Create multiply stored procedures at once from multiply files in SQL Server 2008

I have a large amount of stored procedures that I am updating often and then transferring to a duplicate database on another server. I have been opening each “storedproc.sql” file from within SQL Server Management Studio 2008 and then selecting Execute in the tool bar which will ether create or alter an existing stored procedure. I have been doing this for each stored procedure.
I am looking for a script (or another way) that will allow me to alter all of the stored procedures on the databases with ones that are located in a folder at one time. I am basically looking for a script that will do something similar to the pseudo-code like text below.
USE [DatabaseName]
UPDATE [StoredProcName]
USING [directory\file\path\fileName.sql]
UPDATE [StoredProcNameN]
USING [directory\file\path\fileNameN.sql
…
Not the cleanest pseudo-code but hopefully you understand the idea. I would even be willing to drop all of the stored procedures (based on name) and then create the same stored procedures again on the database. If you need more clarity don’t hesitate to comment, I thank you in advance.
To further explain:
I am changing every reporting stored procedure for an SSRS conversion project. Once the report is developed, I move the report and the stored procedure to a server. I then have to manually run (ALTER or CREATE) each stored procedure against the duplicated database so the database will now be able to support the report on the server. So far this has not been too much trouble, but I will eventually have 65 to 85 stored procedures; and if I have to add one dataset field to each one, then I will have to run each one manually to update the duplicate database.
What I want to be able to do is have a SQL script that says: For this database, ALTER/CREATE this named stored procedure and you can find that .sql text file with the details in this folder.
Here is some code that I use to move all stored procedures from one database to another:
DECLARE #SPBody nvarchar(max);
DECLARE #SPName nvarchar(4000);
DECLARE #SPCursor CURSOR;
SET #SPCursor = CURSOR FOR
SELECT ao.name, sm.definition
FROM <SOURCE DATABASE>.sys.all_objects ao JOIN
<SOURCE DATABASE>.sys.sql_modules sm
ON sm.object_id = ao.object_id
WHERE ao.type = 'P' and SCHEMA_NAME(ao.schema_id) = 'dbo'
order by 1;
OPEN #SPCursor;
FETCH NEXT FROM #SPCursor INTO #SPName, #SPBody;
WHILE ##FETCH_STATUS = 0
BEGIN
if exists(select * from <DESTINATION DATABASE>.INFORMATION_SCHEMA.Routines r where r.ROUTINE_NAME = #SPName)
BEGIN
SET #query = N'DROP PROCEDURE '+#SPName;
exec <DESTINATION DATABASE>..sp_executesql #query;
END;
BEGIN TRY
exec <DESTINATION DATABASE>..sp_executesql #SPBody;
END TRY
BEGIN CATCH
select #ErrMsg = 'Error creating '+#SPName+': "'+Error_Message()+'" ('+#SPBody+')';
--exec sp__LogInfo #ProcName, #ErrMsg, #ProductionRunId;
END CATCH;
FETCH NEXT FROM #SPCursor INTO #SPName, #SPBody;
END;
You need to put in and as appropriate.
For Reference
c:\>for %f in (*.sql) do sqlcmd /S <servername> /d <dbname> /E /i "%f"
I recommend saving all your stored procedure script files starting with if exists(...) drop procedure followed by the create procedure section. Optionally include a go statement at the end depending on your needs.
You can then use a tool to concatenate all the files together into a single script file.
I use a custom tool for this that allows me to define dependency order, specify batch separators, script types, include folders, etc. Some text editors, such as UltraEdit have this capability.
You can also use the Microsoft Database Project to select batches of script files, and execute them against one or more database connections stored in the project. This is a good starting place that doesn't require any extra software, but can be a bit of a pain regarding adding and managing folders and files within the project.
Using a schema comparison tool such as RedGate's SQL Compare can be useful to synchronize the schema and/or objects of two databases. I don't recommend using this as a best practice deployment or "promote to production" tool though.

Linked Server Query / Dynamic SQL

I currently have a linked server that I am querying in a stored procedure. I have the query working just fine currently however this query will need to change for every branch of code I have. I would like to know what the best method is for derriving the database name I am calling in the cross server query.
Ex:
Server A has a link to server B. Server A contains 3 databases. SRV_A.DB1_DEV, SRV_A.DB2_Trunk, SRV_A.DB3_Prod Each are linked to their Server B counterpart... SRV_B.DB1_DEV, SRV_B.DB2_Trunk, SRV_B.DB3_Prod
Each database on Server A has the same stored procedure. The only thing that changes in the sproc is the cross server select. So SRV_A.DB1_Dev has a select in the sproc that reads:
SELECT foo FROM [SRV_B].[DB1_DEV].[foo_table] WHERE bar = 1
while the stored procedure on the trunk branch would be
SELECT foo FROM [SRV_B].[DB2_Trunk].[foo_table] WHERE bar = 1
Since I would like to have a VS project that will deploy the DB to every branch mentioned I would like to be able to fill in the database name dynamically. The solution I have came up with that is working is to use a series of IF checks with the CHARINDEX function, and then create the query with dynamic SQL, like this:
DECLARE #dSql NVARCHAR(4000);
DECLARE #databaseName NVARCHAR(100) = DB_NAME();
DECLARE #tableName NVARCHAR(100);
IF SELECT CHARINDEX('Dev', #databaseName, 0)
SET #tableName = '[SRV_B].[DB1_DEV].[foo_table]
...Same if & set for Trunk
...Same if & set for Prod
SET #dSql = 'DECLARE #retID INT;SELECT foo FROM ' + #tableName
+ ' WHERE bar = 1';SET #retID = SELECT SCOPE_IDENTITY()'
EXEC(#dSQL);
I would have to imagine there is a better solution though, if anyone can help me with one, it would be much appreciated. If by some outside shot this is the best way let me know as well.
Thanks,
James
One way to solve this problem might be to abstract the linked server name by wrapping it in a synonym:
note the extra part in the target table name - cross-server queries require a four-part name - I'm assuming this is a typo in the question and that foo_table is in the dbo schema
CREATE SYNONYM dbo.syn_foo_table
FOR [SRV_B].[DB1_DEV].[dbo].[foo_table]
which could then be referred to in the code as
SELECT foo FROM dbo.syn_foo_table WHERE bar = 1
You would then need to customise your deployment script to create the synonym(s) pointing at the correct server/database for the environment. This could use a similar dynamic SQL process to the one you've outlined above, but would only need to be executed once at deployment time (rather than on every execution).
Another possible solution is to use SQLCMD parameters in the stored procedure script, since (AFAIK) VS projects use SQLCMD to deploy database objects.
This feature allows you to parameterise SQL scripts with variables in the form $(variablename) - in your case:
SELECT foo FROM [SRV_B].[$(dbname)].[foo_table] WHERE bar = 1
The value of the variable can be set using an environment variable or passed into the command as an argument using the -v switch. See the SQLCMD MSDN link above for full details.
I was able to use a combination of enviornment variables as mentioned above for the db-name, and also dynamically generate the SRV name as well by using the following query:
DECLARE #ServerName NVARCHAR(100);
SET #ServerName = (SELECT name FROM sys.servers WHERE server_id = 1)

Copy trigger from one database to another

Is it possible, in a script executed in MS SQL Server 2005, to copy a trigger from one database to another?
I've been asked to write a test script for a trigger my project is using. Our test structure is to create an empty database containing only the object under test, then execute a script on that database that creates all the other objects needed for the test, fills them, runs whatever tests are needed, compares the results against expected results, and then drops everything except the object under test.
I can't just create a database that is empty except for the trigger, because the trigger depends on several tables. My test script currently runs the CREATE TRIGGER after all the required tables are created, but this won't do because the test script isn't allowed to contain the object under test.
What's been suggested is that, instead of running a CREATE TRIGGER, I somehow copy the trigger at that point in the script from the live database to the test database. I've had a quick Google and haven't found a way to do this. Thus my question - is this even possible, and if so, how can I do it?
You could read the text of the trigger with sp_helptext (triggername)
Or you can select the text into a variable and execute that:
declare #sql varchar(8000)
select #sql = object_definition(object_id)
from sys.triggers
where name = 'testtrigger'
EXEC #sql
I have a stored procedure that copies a bunch of tables to a test database. To make it less prone to mistakes that could potentially change the wrong database, I want to avoid using USE and instead explicitly specify per statement which database the trigger is copied from and to.
With the help of this answer, I came up with this solution:
DECLARE #sql NVARCHAR(MAX);
EXEC SourceDB.sys.sp_executesql
N'SELECT #output = (SELECT OBJECT_DEFINITION(OBJECT_ID(''TriggerName'')))',
N'#output VARCHAR(MAX) OUTPUT',
#output = #sql OUTPUT;
EXEC DestDB.sys.sp_executesql #sql;

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.