I've created a SQL agent job in our SQL Server 2008 that executes the following code:
BEGIN
SET NOCOUNT ON;
declare #database nchar(20);
DECLARE Database_Cursor CURSOR FOR
SELECT [name]
FROM master.dbo.sysdatabases
ORDER BY [name]
OPEN Database_Cursor;
FETCH NEXT FROM Database_Cursor INTO #database;
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC [dbo].[sp_BackupDatabase]
#databaseName = #database
FETCH NEXT FROM Database_Cursor INTO #database;
END;
CLOSE Database_Cursor;
DEALLOCATE Database_Cursor;
END
GO
Basically I retrieve a list of databases and execute a backup script for each database.
This script works for most database, but some databases return an error:
Msg 911, Level 16, State 11, Line 1
Database 'appName_Configuration' does not exist. Make sure that the name is entered correctly.
This is correct, this database does not exist. We have several database named like this:
appName_Configuration1
appName_Configuration2
...
For some reason, the script doesn't pass the name including the number to the backup script. If I replace the #database variable with a static name (appName_Configuration1) it works just fine.
Also worth mentioning: there are a few other databases that have a number at the end, which work fine:
appName_Microsoft1
appName_Microsoft2
I suspect that the word "Configuration" may have something to do with it, but renaming the database is not an option for now. Can anyone help me finding a solution so that the name is passed to the stored procedure correctly ?
Kind regards,
Mathew
Increase the size of #database variable as per your database name maximum length can be...
e.g.
declare #database nchar(100);
You declared #database as nchar(20). Your names are longer. Use nvarchar(50), for example.
Related
i am handing over a project to a another party which i have been doing for some time, in this project i do some modifications to some of the existing table by adding coloumns, renaming coloumns etc.
When i was handling the project what i did was, putting the changes or the modifications inside a stored proceedure once it was run calling the function from the query browser.
stored proceedure
CREATE DEFINER=`my_db`#`10.%` PROCEDURE `alter_test_1`()
BEGIN
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE v_table VARCHAR(100) DEFAULT "";
DECLARE stmt VARCHAR(500) DEFAULT "";
DECLARE column_cursor CURSOR FOR
SELECT TABLE_NAME FROM `information_schema`.`tables` WHERE table_schema = 'my_db'
AND table_name LIKE 'tot_table_%';
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished = 1;
OPEN column_cursor;
alter_tables: LOOP
FETCH column_cursor INTO v_table;
IF v_finished = 1 THEN
LEAVE alter_tables;
END IF;
SET #prepstmt = CONCAT('ALTER TABLE my_db','.',v_table,' CHANGE OS platform VARCHAR(25);');
PREPARE stmt FROM #prepstmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP alter_tables;
CLOSE column_cursor;
END
afterwards i run
call alter_test_1();
in the query browser.
Now as im handing this over this two step excution is not professional therefore is there a way for me to do this by using another stored proceddure instead running a call alter_test_1() separately... what i mean is there a way to put this call or several call statements inside a stored proceedure and excecute all the call statements in one shot, once that particular stored proceedure is run.
Well... you call a stored procedure within another stored procedure... by calling it.
CREATE PROCEDURE `do_things`()
BEGIN
CALL `alter_test_1`();
CALL `do_more_stuff`();
END
Executing CALL do_things(); will first run your procedure, then one called "do_more_stuff" and then will return. It will terminate at the first unhandled error thrown unless you catch the error with a HANDLER.
That seems like the answer to the question you asked. I'm less clear about whether that was the question you intended to ask, because you talk about "2 step execution," and I don't see what the 2 steps are.
If you are asking how to run a stored procedure (step 2?) without declaring it first (step 1?), then, no, you can't... it's a "stored" procedure, and has to be stored before it can be executed.
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...
To secure the database interaction, I have created a restricted user who can only execute the stored procedures. He doesn't have any rights to query any table.
This scenario I have implemented very well!. Now the problem is, one of my stored procedure involves dynamic SQL execution, which fails the execution by saying that I don't have the permission to run SELECT query on table X.
To avoid this, I need to provide explicit SELECT permission to such procedures NOT TO USERS.
Please help me!
There is no build in function to give execute to a user.
Create a role with execute permission and add that role to the user.
CREATE ROLE db_executer
GRANT EXECUTE to db_executer
EXEC sp_addrolemember N'db_executer', N'<username>'
MNagel has the correct link for this, but to reiterate, you want to look at using something like:
CREATE PROC dbo.MyProc
WITH EXECUTE AS 'SomeUser'
AS
BEGIN --Proc
--Do Stuff
END --Proc
GO
"Execute As" has some other gotchas along the way. You have to allow others to impersonate the specified user and that user would need the appropriate select permissions to the underlying tables.
If you want to mass grant permissions, you can do that at the Schema level instead of the object level - something like:
GRANT SELECT, EXEC ON Schema::dbo TO MyRole;
I've used that before to greatly simplify a lot of grant statements for our roles. We have very few overrides for the more general roles so this helps quite a bit.
define the stored procedure to be executed as a user with appropriate rights:
http://msdn.microsoft.com/en-us/library/ms188354.aspx
Is this what you want?
USE DatabaseName
GO
-- 1 - db_executestoredprocedures
-- 1a - Create role
CREATE ROLE db_executestoredprocedures
GO
-- 1b - Grant permissions
GRANT EXECUTE TO db_executestoredprocedures
GO
-- 2 - db_selecttablevaluedfunctions
-- 2a - Create role
CREATE ROLE db_selecttablevaluedfunctions
GO
-- 2 - Create permissions
DECLARE #Function_Name nvarchar(250);
DECLARE #CMDEXEC1 nvarchar(2000);
DECLARE db_cursor CURSOR FOR
SELECT [name]
FROM sys.objects
WHERE Type = 'TF'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #Function_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #CMDEXEC1 = 'GRANT SELECT ON [' + #Function_Name + '] TO db_selecttablevaluedfunctions;'
--- SELECT #CMDEXEC1
EXEC(#CMDEXEC1)
FETCH NEXT FROM db_cursor INTO #Function_Name
END
CLOSE db_cursor
DEALLOCATE db_cursor
GO
On the SP where sql is complaining about you not having the permissions to run a SELECT query on table X, do you have a broken chain of ownership on that particular SP and table?
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 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