I have a very basic test stored procedure, shown below. This proc is reading data in a different database, in a different server. To do this I am using a linked server. From what I have read, I need to change the FROM clause to this:
[linked server name].[database name].[schema name].[table name]
However, I would like to pass in the linked server name and database name as parameters and use them in my FROM clause. I am not concerned with injection attacks, etc. I will be passing this in from a config file.
create PROC [dbo].[SelectTEST]
#GU UNIQUEIDENTIFIER,
#LINKED_SERVER_NAME nvarchar(max),
#DATABASE_NAME nvarchar(max)
AS
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRAN
SELECT [GU]
FROM '[' + #LINKED_SERVER_NAME +'].['+ #DATABASE_NAME + '].[Test Table] '
WHERE ([GU] = #GU OR #GU IS NULL)
COMMIT
This is a big mess of syntax errors. Is it possible to pass in these parameters and use in my stored procedure? I would have to make this change to a bunch of different procs, so sorta trying to find the a succinct solution...
Related
I am a complete beginner in terms of SSIS packages.
I really want to execute a stored procedure that takes in parameters with different values at each iteration of the foreach loops. So I'm wondering if anyone can give me an example (VERY VERY VERY basic example) on how I can use variables as values inside an Execute SQL Task like this:
UPDATE tbName SET c1 = Var1, C2 = Var2 etc...
OR
#bDate = VarDate1
#eDate = VarDate2
where Var2, VarDate1, VarDate2 are variables in BIDS
First you need to create the stored procedure on the SQL Server end. This is done with a statement like this. After this has been ran a new stored procedure object called "yourProcedure" will exist in the database.
CREATE PROCEDURE yourProcedure
#pKeyVar int, /* declare variables to be passed here */
#pFirstVar varchar(40),
#pSecondVar int,
#pThirdVar decimal(18,2)
AS
BEGIN
Update yourTable Set /* place what will be done here */
Col1 = #pFirstVar,
Col2 = #pSecondVar,
Col3 = #pThirdVar
WHERE KeyColumn = #pKeyVar
END
GO
Once the stored procedure has been created you can call it like this:
exec dbo.yourProcedure 12345, 'string value', 2, 2.05
There are a couple ways to call it from SSIS, but the most common is the Execute SQL Task. You can map the parameters that will be passed to the variables that hold the content and put the task inside your looping logic.
Here is a decent walkthrough of the Execute SQL Task.
Pay close attention to the section on mapping parameters to variables etc. The version of SSIS is 2005 but the concepts are all the same.
Update after comment.
In order to loop through a recordset and obtain values to pass back to the proc you can follow the information as provided in this article.
I am currently using the CONTEXT_INFO property of the Master database for storing the logged in username to use it later in Table Triggers for auditing.
While migrating to SQL Azure, the issue of Cross-Database connections popped and I couldn't find direct solutions to this issue.
Following are the Issue Details:
I call Stored Procedure XXX from Data Access Layer and pass the Username as Parameter
The username is used to set the CONTEXT_INFO value in XXX
The CONTEXT_INFO value is then used in Tables Insert/Update/Delete Triggers to Store Username for Application Auditing
Solutions that I found so far:
Create Table In Database to work as CONTEXT_INFO
Use 2 Connection Strings in Data Access Layer, one for Master Database (to set CONTEXT_INFO) and the other is for the application and execute the SET CONTEXT_INFO each time before opening the connection to my application
But I find both solutions risky, specially when expanding the Database over multiple SQL Azure Databases in the future.
Appreciate your support.
The approach I took is shown below. On trick was to check to see running not running on SQL Azure, then we would need to call 'SET CONTEXT_INFO ...'. This allows the same code to be execute on local SQL Server Express and Azure without changes.
Create a table to store the context info (not in master but in the same database)
CREATE TABLE [dbo].[ContextInfo] (
[ContextInfo] varbinary(128) not null,
[ApplicationUsername] nvarchar(128) not null,
[UpdatedAt] datetime NOT NULL,
CONSTRAINT [PK_UserContextInfo] PRIMARY KEY CLUSTERED ([ContextInfo] ASC)
)
Create a stored procedure to 'Set Context Info' which is called from application
CREATE PROCEDURE [dbo].[SetContextInfo]
#ApplicationUsername nvarchar(128)
AS
SET NOCOUNT ON
-- Remove all context items older than an 5 minutes ago
DELETE
FROM [dbo].[ContextInfo]
WHERE [UpdatedAt] < DATEADD(mi, -5, GETUTCDATE())
--
-- Use the MERGE command to do an update/insert
-- See: http://technet.microsoft.com/en-us/library/bb510625.aspx
--
IF SERVERPROPERTY('edition') <> 'SQL Azure'
BEGIN
DECLARE #b varbinary(128)
SET #b = CONVERT(varbinary(128),newid())
EXEC sp_executesql #statement=N'SET CONTEXT_INFO #b',#params=N'#b varbinary(128)',#b=#b
END
DECLARE #ContextInfo varbinary(128)
SELECT #ContextInfo = CONTEXT_INFO()
MERGE [dbo].[ContextInfo] AS target
USING (SELECT #ContextInfo, #ApplicationUsername) AS source ([ContextInfo], [ApplicationUsername])
ON (target.[ContextInfo] = source.[ContextInfo])
WHEN MATCHED THEN
UPDATE SET [ApplicationUsername] = source.[ApplicationUsername], [UpdatedAt] = GETUTCDATE()
WHEN NOT MATCHED THEN
INSERT ([ContextInfo], [ApplicationUsername], [UpdatedAt])
VALUES (source.[ContextInfo], source.[ApplicationUsername], GETUTCDATE());
Create a stored procedure to 'Get Context Info'
CREATE PROCEDURE [dbo].[GetContextInfo]
AS
SET NOCOUNT ON
DECLARE #ContextInfo varbinary(128)
SELECT #ContextInfo = CONTEXT_INFO()
SELECT [ApplicationUsername]
FROM [dbo].[ContextInfo]
WHERE [ContextInfo] = #ContextInfo
GO
In trigger source, use:
DECLARE #UserContext TABLE ([Username] VARCHAR(128))
INSERT INTO #UserContext (Username)
EXEC [dbo].[GetContextInfo]
Now you have the username stored in the table variable. In case changes are applied by an administrator outside of your application, you may also want to check if the username was not set and default to something like *SYSTEM_USER*.
I have written a T-SQL script that migrates some data from one database to another.
At the moment I am doing that by use of dynamic sql.
For example see the following code:
Declare #sqlquery nvarchar(4000)
SET #sqlquery = N'SELECT * from ' + #LinkServerName + #SourceDatabaseName + '.dbo.Table'
EXEC #sqlquery
In this example #LinkServerName is a nvarchar variable that stores the name of the linked server for the SQL Server that contains the source database. #SourceDatabaseName is a nvarchar variable that stores the name of the source database.
I donĀ“t like that way. I would prefer the following code:
SELECT * from #SourceDatabase.dbo.Table
Is that possible?
Thank you in advance.
Second approach is incorrect, first one is the correct one. For more information check this other question here at stackoverflow how-to-use-variable-for-database-name-in-t-sql
I am developing an app that will provide a separate database for each subscriber. When a new database is needed a stored proc on the master db fires. It creates a new db and default tables. So far so good. Now I need to copy over several stored procs from the master db to the newly created db. I do not want to maintain scripts or use 3rd party tools, it needs to be dynamic.
Right now I am grabbing the SP contents from sql_modules then attempting to exec it against the new db. Problem is I dont' know how to change the database that exec() fires against, the default db when this stored proc is run is the Master, I need it to be the target. I've tried changing the procedure declaration to CREATE PROCEDURE [MyNewDb].[dbo].[AwesomeSP] but sql complains
'CREATE/ALTER PROCEDURE' does not allow specifying the database name
as a prefix to the object name.
ANSWER FOLLOWS:
Arghh! This was easier than expected thanks to this blog post found on kodyaz.com. Hopefully it will help someone else.
Here is code that copies all sp's in the Master database to the target database, you can copy just the sp's you like by filtering the query on procedure name.
#sql is defined as nvarchar(max)
#Name is the target database
Code:
DECLARE c CURSOR FOR
SELECT Definition
FROM [ResiDazeMaster].[sys].[procedures] p
INNER JOIN [ResiDazeMaster].sys.sql_modules m ON p.object_id = m.object_id
OPEN c
FETCH NEXT FROM c INTO #sql
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = REPLACE(#sql,'''','''''')
SET #sql = 'USE [' + #Name + ']; EXEC(''' + #sql + ''')'
EXEC(#sql)
FETCH NEXT FROM c INTO #sql
END
CLOSE c
DEALLOCATE c
What will work is executing dbname.dbo.sp_executesql N'CREATE PROCEDURE ...'. So, you could build a string that does all that. You could replace your SET #sql = 'Use... with:
set #sql = N'execute ' + QUOTENAME(#name) + N'.dbo.sp_executesql N''' + #sql + ''''
This works for me :
set #script=#destDB+'.dbo.sp_executesql N'''+#spDefenition+''''
exec sp_executesql #script
I've a stored proc like:
CREATE PROCEDURE [dbo].[GetData]
#code varchar(10)
AS
BEGIN
SET NOCOUNT ON;
--code goes here
END
the proc reads data from one of n tables, based on #code passed. So I've a map linking codes with the actual table names, ex.
Code:"A" => dbo.JAN_SALES
Code:"B"=> dbo.FEB_SALES
All tables have the same structure. I know how to get it done by using 'red' sql, wonder if there's more elegant/performant way of doing that with SQL Server 2008?
Edit 1 - Red sql is the sql, which gets built by concatenating its parts and executed by calling something like exec('select A. B, C from ' + #myTable).
Your question seems to be clear on what needs to be done, and there are no much more posibilities than creating the T-SQL code by
1) adding IF blocks evaluating the #code parameter; or
2) using Dynamic Sql ( dynamic sql = "red" ). Please note the dynamic sql is strongly discouraged for production environments.
BTW - On SQL Server 2008 you can use *sp_executesql* proprietary stored procedure. Microsoft's MSDN describes how it works here.